This is the 13th day of my participation in Gwen Challenge

Pay attention to my public number “ananzhuo” free learning knowledge

Git address:

Github.com/ananananzhu…

Summary of Room

Room provides an abstraction layer on top of SQLite for smooth database access while taking full advantage of SQLite’s power.

Room analysis

Annotations and functions in Room

  1. @ the Database:

The @DATABASE annotation class must be an abstract class that extends RoomDatabase;

Contains an abstract method that takes zero arguments and returns a class annotated with @DAO;

By calling the Room. DatabaseBuilder () or Room. InMemoryDatabaseBuilder () access to the Database instance;

  1. @Entity

    Represents a table in a database.

  2. @Dao

Contains methods to access the database

InMemoryDatabaseBuilder and databaseBuilder

In fact, both are pretty well written in the comments

InMemoryDatabaseBuilder A database created in this way can only exist in memory and will be emptied once the application is closed

Let me prove it to you:

The GIF above does just that

Get the database DB object using inMemoryDatabaseBuilder and databaseBuilder respectively, and then insert the data on the two action pages and check if the data was successfully inserted

After closing the application and opening it again, database objects retrieved from databaseBuilder can still be queried, and database objects retrieved from inMemoryDatabaseBuilder cannot be queried. This confirms the statement in the comment and is consistent with our previous conclusion

Room use

Simple to use

In this example, we define a product called Goods. The product contains an increment ID and an increment name. Then, we can add, query, and delete the product. Main display goods to add and query

  1. Room rely on
"Implementation (" Androidx. room: room-Runtime :$room_version"  "androidx.room:room-compiler:$room_version" // To use Kotlin annotation processing tool (kapt) kapt("androidx.room:room-compiler:$room_version") // To use Kotlin Symbolic Processing (KSP) ksp("androidx.room:room-compiler:$room_version") // optional - Kotlin Extensions and Coroutines support for Room implementation("androidx.room:room-ktx:$room_version") // optional - RxJava2 support for Room implementation "androidx.room:room-rxjava2:$room_version" // optional - RxJava3 support for Room implementation "androidx.room:room-rxjava3:$room_version" // optional - Guava support for Room, including Optional and ListenableFuture implementation "androidx.room:room-guava:$room_version" // optional - Test helpers testImplementation("androidx.room:room-testing:$room_version") }Copy the code
  1. Define RoomDatabase class
@Database(entities = arrayOf(Goods::class),version = 1)
abstract class GoodsDatabase: RoomDatabase() {
   abstract fun goodsDao():GoodsDao
}
Copy the code
  1. Define the Dao class
@Dao
interface GoodsDao {
    @Query("SELECT * FROM goods")
    fun getAll(): List<Goods>

    @Query("SELECT * FROM goods WHERE id IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<Goods>

    @Insert
    fun insertAll(vararg users: Goods)

    @Delete
    fun delete(user: Goods)
}
Copy the code
  1. Define the Goods class
@Entity
data class Goods(
    @ColumnInfo(name = "goodsName") val name: String
) {
    @PrimaryKey(autoGenerate = true)
    var id: Long? =null
}
Copy the code

Since the primary key of the item is automatically accumulated, it should not be passed in the constructor, so we set it initially to NULL.

Note: Must be set to null to implement increment, if set to a specific value then increment will not be possible

  1. Insert data
Globalscope.launch {db.goodsdao ().insertall (Goods(" Anagalenia "))}Copy the code
  1. Take the data and present it
Globalscope.launch {val all = db.goodsdao ().getall () val sb = StringBuilder() for (good in all){sb.append(" ${good.id} ${good.name} \n")} withContext(dispatchers.main){tv_simpleuse.text= sb.tostring ()}}Copy the code
  1. Display effect

Key knowledge points

Entity

  1. Fields in @Entity can be ignored using the @ignore annotation if we do not want to generate a column in the database
@Ignore val time:Long=0
Copy the code
  1. A method by which a class inherits a superclass and the database does not generate columns for a superclass field
open class User {
        var picture: Bitmap? = null
    }

    @Entity(ignoredColumns = arrayOf("picture"))
    data class RemoteUser(
        @PrimaryKey val id: Int,
        val hasVpn: Boolean
    ) : User()
Copy the code

In this case, RemoteUser inherits User, which has a picture column, and we don’t want the database table to generate a picture column, so add ignoredColumns to RemoteUser’s Entity annotation.

  1. How do I add order to an index in an entity class

Add the following code to the annotation when defining the entity class:

@Entity(indices = arrayOf(Index(value = ["last_name", "address"])))
Copy the code

Dao (insert query operation, part is copied from the official website)

How do I set up database queries that can be called in the main thread

The allowMainThreadQueries method is called when building db objects

It can be set, but you are advised not to do it 100% of the time

 val db =
            Room.databaseBuilder(applicationContext, GoodsDatabase::class.java, "goods")
                .allowMainThreadQueries()
                .build()
Copy the code
Insert method

Methods that insert data can take one or more arguments and return the primary key if it is a single argument or an array of primary keys if it is multiple arguments

@Insert(onConflict = OnConflictStrategy.REPLACE)
        fun insertUsers(vararg users: User)
Copy the code
Update the data
@Dao
    interface MyDao {
        @Update
        fun updateUsers(vararg users: User)
    }
Copy the code
Delete the data
@Dao
    interface MyDao {
        @Delete
        fun deleteUsers(vararg users: User)
    }
Copy the code
The query
A simple query
    @Dao
    interface MyDao {
        @Query("SELECT * FROM user")
        fun loadAllUsers(): Array<User>
    }
Copy the code
Query with query criteria
    @Dao
    interface MyDao {
        @Query("SELECT * FROM user WHERE age > :minAge")
        fun loadAllUsersOlderThan(minAge: Int): Array<User>
    }
Copy the code

: The minAge binding parameter matches the minAge method parameter

Return a subset of the list

The following code query results are automatically assigned to the NameTuple

@Dao
    interface MyDao {
        @Query("SELECT first_name, last_name FROM user")
        fun loadFullName(): List<NameTuple>
    }
Copy the code

In most cases, you only need to get a few fields of the entity. For example, your interface might display only the user’s first and last name, rather than every detail of the user.

Query an indefinite number of parameters

Some queries may require you to pass in an indefinite number of parameters, the exact number of which is not known until run time. For example, you might want to retrieve information about all users from a section. Room knows when a parameter represents a collection and automatically expands it at run time based on the number of parameters provided.

    @Dao
    interface MyDao {
        @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
        fun loadUsersFromRegions(regions: List<String>): List<NameTuple>
    }

Copy the code
Use suspend to modify methods in the DAO

Use the suspend modifier to make these methods asynchronous through coroutines to prevent us from operating on the main thread

 @Update
        suspend fun updateUsers(vararg users: User)
Copy the code
Use Livedata to observe database operations
 @Dao
    interface MyDao {
        @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
        fun loadUsersFromRegionsSync(regions: List<String>): LiveData<List<User>>
    }

Copy the code

Define relationships between objects

Without going into this for the moment, I will go back to the article:

Official website reference:

Developer. The android. Google. Cn/training/da…

Prefilled database

Pre-installation from assets directory

To pre-populate the Room database from a pre-wrapped database file located anywhere in the application assets/ directory, call the createFromAsset() method from the RoomDatabase.Builder object, and then call build()

 Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
        .createFromAsset("database/myapp.db")
        .build()

Copy the code

Preinstall from file

    Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
        .createFromFile(File("mypath"))
        .build()

Copy the code

Database Migration

The Room persistence library supports incremental Migration through the Migration class to meet this requirement. Each Migration subclass defines the Migration path between startVersion and endVersion by replacing the migration.migrate () method. When an application update requires a database upgrade, Room runs the Migrate () method from one or more Migration subclasses to migrate to the latest version at run time:

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`))")
    }
}

val MIGRATION_2_3 = object : Migration(2, 3) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")
    }
}

Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name")
        .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build()
Copy the code

The website provides unit tests to test data migration with as few errors as possible

Developer. The android. Google. Cn/training/da…