1. Introduction

Since Google launched Room, we can use SQLite database gracefully. Of course, with the combination of Room+Paging, the code is written and flies. Please refer to the tutorial below for details on how to use Room+Paging. Paging in Android: juejin.cn/post/684490…

Add a reference to the Room library in your App’s build.gradle file.

    def room_version = "2.2.5"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-rxjava2:$room_version"
    implementation "androidx.room:room-testing:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
Copy the code

2. Create the Entity

First we will create a Data class as the Entity class of the database. Then add additional information about the database using the annotations described below.

@Entity

Add an @Entity annotation to the entity you want to create.

@Entity(tableName = "users_table")
data class User(...)
Copy the code

You can also add as many parameters after @Entity as you want.

  1. tableName, you can name the Table. The sample,tableName = "users_table".
  2. primaryKeys, you can specify a primary key for Table. The sample,primaryKeys = {"firstName", "lastName"}.
  3. IndexIn order to speed up the data query speed, you can add indexes. The sample,indices = {@Index("lastName").
  4. uniqueCan be added when the index needs to ensure the uniqueness of information in two columns. The sampleindices = {@Index(value = {"first_name", "last_name"},unique = true)}

@PrimaryKey

The @primaryKey annotation has the same effect as primaryKeys in @Entity above. You can also add autoGenerate=true for auto-added primary keys.

@PrimaryKey(autoGenerate = true)
val id: Int? = null.Copy the code

@ColumnInfo

Add the list name name=”name” for the attributes in the entity.

@ColumnInfo(name = "birthday")
val birthday: String
Copy the code

@Ignore

If you have attributes in your Entity that you do not want to store in the database, you can add the @ignore annotation.

@Ignore
val nationality: String
Copy the code

@Embedded

If there are other objects in the Entity class, you can use @Embedded to nest them. Of course the object to be nested also needs to be an Entity class annotated by @Entity.

@Embedded
val address: Address
Copy the code

3. Create a Dao

@Dao

The full name of Dao is Data Access Object, is a Data Access Object is an object-oriented database interface. In general development, a Dao needs to be abstracted separately. Create an interface in Room and add the @DAO annotation.

@Dao
interface UserDao {}
Copy the code

@Query

You need to add @Query to the method that implements the Query, along with the SQL statement to be queried.

@Query("SELECT * FROM users_table where first_name = :firstName")
suspend fun getUserByFirstName(firstName:String): List<User>
Copy the code

If a method has parameters passed in that need to be used in an SQL query, you can use :parameter to bind the parameters.

@Insert

To Insert data into a database, you need to annotate @INSERT, but you don’t need to add SQL statements. You need to pass in an object inserted into the database as a parameter to the function.

@Insert
suspend fun insertUser(users: List<User>)
Copy the code

@Delete

To Delete data from the database, you need to annotate @delete, but you do not need to add SQL statements. You need to pass the deleted object as an argument to the function.

@Delete
suspend fun deleteUser(users: List<User>)
Copy the code

@Update

The @update annotation is required to Update data in the database, but no SQL statement is required. You need to pass the updated object as an argument to the function.

@Update
public void updateUsers(User... user);
Copy the code

@Transaction

SQLite transactions can be added using the @Transaction annotation.

@Transaction
suspend fun deleteAllAndInsertUser(users: List<User>) {
    deleteUser(getAll())
    insertUser(users)
}
Copy the code

The overall code

@Dao
interface UserDao {

    @Query("SELECT * FROM users_table")
    suspend fun getAll(a): List<User>

    @Insert
    suspend fun insertUser(user: User)
    
    @Update
    public void updateUsers(User... user);

    @Delete
    suspend fun deleteUser(users: List<User>)

    @Transaction
    suspend fun deleteAllAndInsertUser(users: List<User>) {
        deleteUser(getAll())
        users.forEach { insertUser(it) }
    }
}
Copy the code

4. Create RoomDatabase

Example code is as follows:

@Database(version = 1, entities = [User::class])  // Add a Database annotation. You also need to add version information and entities information
@TypeConverters(Converter::class)  // Add TypeConverter annotations
abstract class AppDatabase : RoomDatabase() {

    companion object {
        private const val DB_NAME = "user_database.db"

        private var INSTANCE: AppDatabase? = null
        private var lock = Any()

        fun getInstance(context: Context): AppDatabase {
            synchronized(lock) {
                if (INSTANCE == null) {
                    // The first argument is Context, the second argument is DB class, and the third argument is DB name
                    INSTANCE =
                        Room.databaseBuilder(context, AppDatabase::class.java.DB_NAME).build()}returnINSTANCE!! }}fun destroyInstance(a) {
            INSTANCE = null}}abstract fun getUserDao(a): UserDao
}
Copy the code

The database class needs to inherit from RoomDatabase, and it must declare an abstract class. It is also important to note that creating an instance of a database is very performance critical and cannot be repeated. A common approach to this problem is to design the instance creation method into the Singleton pattern.

@Database

You need to annotate the Database class with @database. The following information is also not required.

  1. versionTo specify the version of the database.
  2. entities, all entity classes need to be specified.
@Database(version = 1, entities = [User::class])
Copy the code

@TypeConverter

Room is a basic data type that supports strings, integers, etc., but when we encounter other data types, we need to use @typeconverter to convert data types. For example, when the birthday attribute of User is Date, the data type conversion is required.

class Converter {
    @TypeConverter
    fun longToDate(timeStamp: Long?).: Date? {
        if (timeStamp == null) return null
        return Date(timeStamp)
    }

    @TypeConverter
    fun dateToLong(date: Date?).: Long? {
        if (date == null) return null
        return date.time
    }
}
Copy the code

5. Database upgrade and Migration

When the App has been published and needs to update and change the database structure, it needs to use the data migration of the database. Room provides us with the function of database upgrade.

    Upgrade from version 1 to version 2
    val MIGRATION_1_2 = object : Migration(1.2) {
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, `name` TEXT, " +
                    "PRIMARY KEY(`id`))")}}// Upgrade from version 2 to version 3
    val MIGRATION_2_3 = object : Migration(2.3) {
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")}}// Add the version upgrade to Room builder
    Room.databaseBuilder(applicationContext, MyDb::class.java."database-name")
            .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build()
    
Copy the code

6. Github

Making: github.com/HyejeanMOON…

Property Animation for Android: juejin.cn/post/684490… Android ConstraintLayout: juejin.cn/post/684490… In RecyclerView can cope with multiple ViewType libraries -Groupie: juejin.cn/post/684490… Use of Google MergeAdapter: juejin.cn/post/684490… Paging in Android: juejin.cn/post/684490… Android UI testing Espresso: juejin.cn/post/684490… Android WorkManager: juejin.cn/post/684490…