快速了解Android?Room使用細(xì)則進(jìn)階
1、前言
上一篇5分鐘帶你了解Android Room數(shù)據(jù)好起來了,有人催更,遂決定再寫一篇Room的使用,這篇我們著重講講注解。如果寫的不好,或者有錯誤之處,懇請在評論、私信、郵箱指出,萬分感謝??
2、@ForeignKey和@PrimaryKey
考驗?zāi)銛?shù)據(jù)庫知識的時候來了!因為你會頻繁看到@PrimaryKey
所以先講它
@ForeignKey
注解用于定義外鍵關(guān)系,它指定了一個實體類中的一個字段是另一個實體類的主鍵。這種關(guān)系被稱為“外鍵關(guān)系”,并且可以用于在多個表之間建立關(guān)聯(lián)。
例如,如果有兩個實體類 User
和 Address
,并且想要將它們關(guān)聯(lián)起來,則可以使用 @ForeignKey
注解來指定 Address
中的 user_id
字段是 User
的主鍵:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String ) @Entity(tableName = "addresses", foreignKeys = [ ForeignKey(entity = User::class, parentColumns = ["id"], childColumns = ["user_id"], onDelete = ForeignKey.CASCADE) ]) data class Address( @PrimaryKey val id: Int, val street: String, val city: String, val state: String, val zip: String, @ColumnInfo(name = "user_id") val userId: Int )
在這個例子中,我們使用 @ForeignKey
注解將 Address
中的 user_id
字段指定為 User
的主鍵。這將創(chuàng)建一個外鍵關(guān)系,確保在插入或更新 Address
表中的數(shù)據(jù)時,user_id
字段的值必須是 User
表中存在的主鍵值之一。
@PrimaryKey
注解用于指定實體類中的一個字段是主鍵。主鍵是用于唯一標(biāo)識每個實體類對象的字段。在 Room 中,每個實體類必須至少有一個字段被指定為主鍵。
例如,如果有一個實體類 User
,并且想要將 id
字段指定為主鍵,則可以使用 @PrimaryKey
注解:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String )
在這個例子中,我們使用 @PrimaryKey
注解將 id
字段指定為 User
實體類的主鍵。這將確保在插入或更新 User
表中的數(shù)據(jù)時,每個 id
字段的值都是唯一的。
3、@TypeConverters
在使用Room時,你可能會遇到需要在Entity類中使用非基本類型的情況,例如Date、Calendar、List等類型。在這種情況下,你可以使用TypeConverters將這些類型轉(zhuǎn)換為Room可以存儲的類型。在Room中,可以使用@TypeConverter注解來定義一個類型轉(zhuǎn)換器,例如:
class Converters { @TypeConverter fun fromDate(date: Date): Long { return date.time } @TypeConverter fun toDate(timestamp: Long): Date { return Date(timestamp) } @TypeConverter fun fromList(list: List<String>?): String? { return list?.joinToString(",") } @TypeConverter fun toList(string: String?): List<String>? { return string?.split(",") } } @Entity(tableName = "user") @TypeConverters(Converters::class) data class User( @PrimaryKey val id: Int, val name: String, val birthday: Date @TypeConverters(HobbiesConverter::class) val hobbies: List<String> ) @Database(entities = [User::class], version = 1) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { // ... } @Dao @TypeConverters(Converters::class) interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User> }
示例代碼在非常多的地方使用了@TypeConverters
,不同的位置造成的影響也是不同的,實際上可以應(yīng)用到以下四個地方:
- 實體類:在
@Entity
注解中使用,可以在處理該實體類時使用它們。 - DAO 接口:在 DAO 接口中使用,可以在執(zhí)行該 DAO 中的查詢時使用它們。
- 數(shù)據(jù)庫類:在
RoomDatabase
類中使用, 可以在整個數(shù)據(jù)庫中使用它們。 - 實體類中的屬性:在實體類中的屬性,可以在處理該屬性時使用指定的類型轉(zhuǎn)換器
4、@Relation
@Relation
用于在實體類之間建立關(guān)系。它可以用于定義兩個或更多實體之間的關(guān)系,這些實體可以在數(shù)據(jù)庫中分別存儲在不同的表中。
@Relation
注解應(yīng)該與 @Query
注解一起使用,以便 Room 可以在查詢結(jié)果中返回相關(guān)實體之間的關(guān)系。@Relation
注解的一個常見用例是定義父子關(guān)系,其中一個實體包含對另一個實體的引用。
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String, val email: String ) @Entity(tableName = "books") data class Book( @PrimaryKey val id: Int, val title: String, val author: String, val userId: Int ) data class UserWithBooks( @Embedded val user: User, @Relation( parentColumn = "id", entityColumn = "userId" ) val books: List<Book> )
在這個示例中,我們有兩個實體類 User
和 Book
,它們之間有一個父子關(guān)系,其中一個用戶可以擁有多本書。然后,我們定義了一個 UserWithBooks
數(shù)據(jù)類,它包含一個嵌套的 User
實體和一個 @Relation
注解,用于指定如何檢索與該用戶關(guān)聯(lián)的所有書籍。@Relation
注解包括 parentColumn
和 entityColumn
參數(shù),分別指定父實體的主鍵列和子實體的外鍵列。
當(dāng)我們使用 @Relation
注解時,我們需要在查詢中使用 SELECT
語句,以便 Room 可以檢索相關(guān)的實體。例如,在 Dao
接口中,我們可以使用以下查詢:
//Transaction下一點就會說 @Transaction @Query("SELECT * FROM users WHERE id = :id") fun getUserWithBooks(id: Int): UserWithBooks
此外,我們使用 SELECT *
語句來檢索所有用戶屬性和相關(guān)的書籍列表,因為 UserWithBooks
數(shù)據(jù)類包含一個嵌套的 User
實體和一個 List<Book>
列表。
5、@Transaction
第4點說到@Relation
時使用到了@Transaction
。在這個查詢中,我們使用 @Transaction
注解來確保整個查詢作為一個事務(wù)執(zhí)行,以便 Room 可以在單個操作中檢索 User
實體和與之相關(guān)的所有 Book
實體。
@Transaction
用于將一組數(shù)據(jù)庫操作包裝在一個事務(wù)中。它可以確保在執(zhí)行數(shù)據(jù)庫操作時保持?jǐn)?shù)據(jù)庫的一致性,并在必要時回滾事務(wù)以確保數(shù)據(jù)的完整性。
在 Room 中,單個數(shù)據(jù)庫操作(例如插入、更新或刪除)是自動運行在事務(wù)中的。但是,當(dāng)需要執(zhí)行多個數(shù)據(jù)庫操作時,可能需要手動創(chuàng)建一個事務(wù)來確保這些操作原子性地執(zhí)行。如果需要執(zhí)行多個數(shù)據(jù)庫操作,請始終考慮使用 @Transaction
注解。這可以避免數(shù)據(jù)不一致和其他與數(shù)據(jù)庫操作相關(guān)的問題。
6、@Embedded
上一篇,有同志說@Embedded很好用,的確如此哈
@Embedded
用于指定一個實體類中的一個或多個字段應(yīng)該作為其所屬的另一個實體的嵌入式對象。這使得 Room 可以將多個相關(guān)實體的數(shù)據(jù)組合成一個單獨的對象,從而簡化了數(shù)據(jù)庫操作。
當(dāng)在一個實體類中使用 @Embedded
注解時,可以指定該實體類中的一個或多個字段應(yīng)該嵌入到另一個實體類中。例如,假設(shè)有一個 Address
實體類和一個 User
實體類,其中 User
實體類包含一個 Address
對象??梢允褂?@Embedded
注解將 Address
對象嵌入到 User
實體類中:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String, @Embedded val address: Address ) data class Address( val street: String, val city: String, val state: String, val zip: String )
在這個例子中,User
實體類包含一個 Address
對象,它使用 @Embedded
注解指定了該對象應(yīng)該嵌入到 User
實體類中。在查詢數(shù)據(jù)庫時,Room 將自動組合 User
實體類和 Address
實體類中的字段,以便可以輕松地訪問和操作它們。
還可以使用 prefix
參數(shù)來指定 Room 應(yīng)該在組合兩個實體類中的字段時使用的前綴。例如:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String, @Embedded(prefix = "home_") val homeAddress: Address, @Embedded(prefix = "work_") val workAddress: Address )
在這個例子中,User
實體類包含兩個 Address
對象,一個是 homeAddress
,另一個是 workAddress
。我們使用 @Embedded(prefix = "home_")
和 @Embedded(prefix = "work_")
注解為每個地址對象指定了不同的前綴。這使得 Room 可以區(qū)分兩個地址對象中的相同字段,并將它們組合成一個單獨的對象。
當(dāng)然你也可以這么干
@Entity(tableName data class User( @PrimaryKey val id: Int, val name: String, @Embedded @ColumnInfo(name = "home_address") val homeAddress: Address )
7、@ColumnInfo
可以看到,我們剛剛用到了@ColumnInfo
這個注解,用于自定義實體類中的列名、默認(rèn)值和其他屬性。當(dāng)需要將一個實體類映射到數(shù)據(jù)庫表時,可以使用 @ColumnInfo
注解來指定實體類中的每個字段在數(shù)據(jù)庫表中的名稱和其他屬性。
(1)指定實體類中的字段名稱
@ColumnInfo
注解最常用的用途是指定實體類中的字段名稱。例如:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, @ColumnInfo(name = "full_name") val name: String, val age: Int )
在這個例子中,我們使用 @ColumnInfo(name = "full_name")
將 name
字段的名稱指定為 full_name
。這意味著在數(shù)據(jù)庫表中,這個字段將被命名為 full_name
,而不是 name
。
(2)指定實體類中的字段默認(rèn)值
@ColumnInfo
注解還可以用于指定實體類中的字段默認(rèn)值。例如:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, @ColumnInfo(name = "full_name") val name: String, @ColumnInfo(name = "is_active") val isActive: Boolean = true )
在這個例子中,我們使用 @ColumnInfo(name = "is_active")
將 isActive
字段的名稱指定為 is_active
,并將其默認(rèn)值設(shè)置為 true
。這意味著在數(shù)據(jù)庫表中,這個字段將被命名為 is_active
,并且默認(rèn)值將為 true
。
(3)指定實體類中的字段約束
@ColumnInfo
注解還可以用于指定實體類中的字段約束。例如:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, @ColumnInfo(name = "full_name") val name: String, @ColumnInfo(name = "is_active") val isActive: Boolean = true, @ColumnInfo(name = "created_at", defaultValue = "CURRENT_TIMESTAMP") val createdAt: String )
在這個例子中,我們使用 @ColumnInfo(name = "created_at", defaultValue = "CURRENT_TIMESTAMP")
將 createdAt
字段的名稱指定為 created_at
,并將其默認(rèn)值設(shè)置為 CURRENT_TIMESTAMP
。這意味著在數(shù)據(jù)庫表中,這個字段將被命名為 created_at
,并且默認(rèn)值將為 CURRENT_TIMESTAMP
。
8、@Ignore
很直觀的注解哈。指定實體類中應(yīng)該忽略的字段。當(dāng)需要在實體類中添加一個字段,但不想將其映射到數(shù)據(jù)庫表中時,可以使用 @Ignore
注解來指定該字段應(yīng)該被忽略。
忽略一個實體類中的字段
@Ignore
注解最常用的用法是忽略一個實體類中的字段,從而防止該字段被映射到數(shù)據(jù)庫表中。例如:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String, @Ignore val password: String )
在這個例子中,我們使用 @Ignore
將 password
字段指定為應(yīng)該被忽略的字段。這意味著在數(shù)據(jù)庫表中,這個字段將不會出現(xiàn),也不會被映射到 User
實體類中。
(1)忽略一個實體類中的 getter 和 setter 方法
除了忽略一個實體類中的字段外,@Ignore
注解還可以用于忽略一個實體類中的 getter 和 setter 方法。這可以幫助控制 Room 如何處理實體類中的方法。
例如,如果希望 Room 不生成一個實體類中的 setter 方法,則可以將 @Ignore
注解添加到該方法上:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String, val password: String ) { @Ignore fun setPassword(password: String) { // ... } }
在這個例子中,我們使用 @Ignore
將 setPassword
方法指定為應(yīng)該被忽略的方法。這意味著 Room 不會生成一個 setter 方法來設(shè)置 password
字段的值。
(2)忽略一個實體類中的整個構(gòu)造函數(shù)
最后,@Ignore
注解還可以用于忽略一個實體類中的整個構(gòu)造函數(shù)。這可以幫助控制 Room 如何處理實體類中的構(gòu)造函數(shù)。
例如,如果希望 Room 不使用默認(rèn)的構(gòu)造函數(shù)來創(chuàng)建實體類的實例,則可以使用 @Ignore
注解標(biāo)記該構(gòu)造函數(shù):
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String, val password: String ) { @Ignore constructor(id: Int, name: String) : this(id, name, "") }
在這個例子中,我們使用 @Ignore
將第二個構(gòu)造函數(shù)指定為應(yīng)該被忽略的構(gòu)造函數(shù)。這意味著 Room 不會使用這個構(gòu)造函數(shù)來創(chuàng)建 User
實體類的實例。
9、@Index
考驗?zāi)銛?shù)據(jù)庫知識的時候來了!索引(個索引、多個索引、復(fù)合索引)可以提高數(shù)據(jù)庫表查詢的性能,因為它們使數(shù)據(jù)庫系統(tǒng)能夠更快地查找和排序表中的數(shù)據(jù)。
(1)在一個實體類中創(chuàng)建單個索引
@Index
注解最常用的用法是在一個實體類中創(chuàng)建單個索引。例如:
@Entity(tableName = "users", indices = [Index(value = ["name"])]) data class User( @PrimaryKey val id: Int, val name: String, val age: Int )
在這個例子中,我們使用 @Index
注解在 name
字段上創(chuàng)建了一個單個索引。這將使數(shù)據(jù)庫系統(tǒng)能夠更快地查找和排序 User
表中的數(shù)據(jù)。
(2)在一個實體類中創(chuàng)建多個索引
除了在一個實體類中創(chuàng)建單個索引外,@Index
注解還可以用于在一個實體類中創(chuàng)建多個索引。例如:
@Entity(tableName = "users", indices = [ Index(value = ["name"]), Index(value = ["age"]) ]) data class User( @PrimaryKey val id: Int, val name: String, val age: Int )
在這個例子中,我們使用 @Index
注解在 name
和 age
字段上創(chuàng)建了兩個索引。這將使數(shù)據(jù)庫系統(tǒng)能夠更快地查找和排序 User
表中的數(shù)據(jù)。
(3)在一個實體類中創(chuàng)建復(fù)合索引
@Index
注解還可以用于在一個實體類中創(chuàng)建復(fù)合索引。復(fù)合索引是指將多個字段組合在一起以創(chuàng)建一個索引,這將使數(shù)據(jù)庫系統(tǒng)能夠更快地查找和排序這些字段的組合。
例如,如果希望在 User
表中按照 name
和 age
字段的組合進(jìn)行排序,則可以使用 @Index
注解來創(chuàng)建一個復(fù)合索引:
@Entity(tableName = "users", indices = [ Index(value = ["name", "age"]) ]) data class User( @PrimaryKey val id: Int, val name: String, val age: Int )
在這個例子中,我們使用 @Index
注解在 name
和 age
字段上創(chuàng)建了一個復(fù)合索引。這將使數(shù)據(jù)庫系統(tǒng)能夠更快地查找和排序 User
表中按照 name
和 age
字段的組合進(jìn)行排序的數(shù)據(jù)。
10、@Entity
當(dāng)在 Room 中定義一個實體類時,必須使用 @Entity
注解來指定該類應(yīng)該被映射到數(shù)據(jù)庫中的哪個表。
(1)在一個實體類中指定表名
@Entity
注解最常用的用法是在一個實體類中指定表名。例如:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, val name: String, val age: Int )
在這個例子中,我們使用 @Entity
注解將 User
實體類映射到名為 users
的數(shù)據(jù)庫表中。這將使 Room 能夠?qū)?User
類中的字段映射到數(shù)據(jù)庫表中的相應(yīng)列中。
(2)在一個實體類中指定索引
除了在一個實體類中指定表名外,@Entity
注解還可以用于在一個實體類中指定索引。索引可以提高數(shù)據(jù)庫表查詢的性能,因為它們使數(shù)據(jù)庫系統(tǒng)能夠更快地查找和排序表中的數(shù)據(jù)。
例如,如果希望在 User
表中按照 name
字段進(jìn)行排序,則可以使用 @Entity
注解來創(chuàng)建一個索引:
@Entity(tableName = "users", indices = [Index(value = ["name"])]) data class User( @PrimaryKey val id: Int, val name: String, val age: Int )
在這個例子中,我們使用 @Entity
注解在 name
字段上創(chuàng)建了一個索引。這將使數(shù)據(jù)庫系統(tǒng)能夠更快地查找和排序 User
表中的數(shù)據(jù)。
(3)在一個實體類中指定繼承關(guān)系
最后,@Entity
注解還可以用于在一個實體類中指定繼承關(guān)系。如果的實體類繼承自另一個實體類,則可以使用 @Entity
注解來指定它們之間的關(guān)系。
例如,如果有一個 Person
實體類和一個 Employee
實體類,Employee
實體類繼承自 Person
實體類,則可以使用 @Entity
注解來指定它們之間的關(guān)系:
@Entity(tableName = "user") open class User( @PrimaryKey val id: Int, val name: String ) @Entity(tableName = "employees") data class Employee( @PrimaryKey val id: Int, val salary: Int, @ColumnInfo(name = "user_id") val userId: Int ) : User(userId, "")
在這個例子中,我們使用 @Entity
注解將 Person
實體類映射到名為 people
的數(shù)據(jù)庫表中,并將 Employee
實體類映射到名為 employees
的數(shù)據(jù)庫表中。此外,我們還使用 @Entity
注解指定了 Employee
實體類繼承自 Person
實體類,并使用 @ColumnInfo
注解將 person_id
字段指定為 Employee
表中的外鍵。這將確保在插入或更新 Employee
表中的數(shù)據(jù)時,person_id
字段的值必須是 Person
表中存在的主鍵值之一。
11、@Dao
@Dao
是用于訪問數(shù)據(jù)庫中數(shù)據(jù)的一種抽象層。在 Room 中,每個 DAO 都定義了一組用于與數(shù)據(jù)庫進(jìn)行交互的方法。意思是就這么用,沒啦。
@Dao interface UserDao {}
12、@Database
@Database
注解是 Room 中的一個注解,用于定義數(shù)據(jù)庫類。當(dāng)在 Room 中定義一個數(shù)據(jù)庫時,必須使用 @Database
注解來指定該數(shù)據(jù)庫包含哪些實體類和版本號等信息。
(1)在一個類中定義數(shù)據(jù)庫實例
@Database
注解最常用的用法是在一個類中定義數(shù)據(jù)庫實例。例如:
@Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
在這個例子中,我們使用 @Database
注解定義了一個數(shù)據(jù)庫類 AppDatabase
,并在其中指定了包含 User
實體類的數(shù)據(jù)庫版本號。此外,我們還定義了一個抽象方法 userDao()
,用于返回一個 UserDao
數(shù)據(jù)訪問對象 (DAO)。
(2)指定多個實體類
@Database
注解還可以用于指定多個實體類。例如:
@Database(entities = [User::class, Address::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao abstract fun addressDao(): AddressDao }
在這個例子中,我們使用 @Database
注解指定了包含 User
和 Address
實體類的數(shù)據(jù)庫版本號。然后,我們定義了兩個抽象方法 userDao()
和 addressDao()
,分別用于返回 UserDao
和 AddressDao
數(shù)據(jù)訪問對象 (DAO)。
(3)指定數(shù)據(jù)庫升級策略
最后,@Database
注解還可以用于指定數(shù)據(jù)庫升級策略。當(dāng)升級數(shù)據(jù)庫時,可能需要指定一些操作來處理數(shù)據(jù)模式的變化。Room 提供了兩種升級策略:Migrate
和 FallbackToDestructiveMigration
。
例如,如果希望在升級數(shù)據(jù)庫時保留現(xiàn)有數(shù)據(jù),可以使用 Migrate
升級策略:
val migration1to2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { // TODO: write migration code here } } @Database(entities = [User::class], version = 2, migrations = [migration1to2]) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
在這個例子中,我們使用 Migrate
升級策略將數(shù)據(jù)庫版本從 1 升級到 2。我們定義了一個名為 migration1to2
的遷移對象,用于在升級數(shù)據(jù)庫時執(zhí)行自定義的 SQL 語句。然后,我們使用 @Database
注解指定了包含 User
實體類的數(shù)據(jù)庫版本號和升級策略。
如果不需要保留現(xiàn)有數(shù)據(jù),可以使用 FallbackToDestructiveMigration
升級策略:
@Database(entities = [User::class], version = 2, exportSchema = false) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao companion object { @Volatile private var instance: AppDatabase? = null fun getInstance(context: Context): AppDatabase { return instance ?: synchronized(this) { instance ?: buildDatabase(context).also { instance = it } } } private fun buildDatabase(context: Context): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, "app-database") .fallbackToDestructiveMigration().build() } } }
在這個例子中,我們使用 FallbackToDestructiveMigration
升級策略將數(shù)據(jù)庫版本從 1 升級到 2。我們使用 @Database
注解指定了包含 User
實體類的數(shù)據(jù)庫版本號和升級策略,并將 exportSchema
參數(shù)設(shè)置為 false
,以避免生成不必要的 JSON
文件。
13、@Query
用于定義SQL查詢語句,可以在之前的例子中找到,許多的@Query
的身影 (考驗?zāi)銛?shù)據(jù)庫基礎(chǔ)的時候到了?。?/p>
(1)基本查詢操作
@Query
注解最常用的用法是執(zhí)行基本的查詢操作。例如:
@Dao interface UserDao { @Query("SELECT * FROM users") fun getAllUsers(): List<User> }
在這個例子中,我們使用 @Query
注解定義了一個基本的 SQL 查詢語句,該語句將返回 users
表中的所有數(shù)據(jù)。我們將此查詢定義為 getAllUsers()
方法的一部分,以便在需要時調(diào)用該方法。
(2)帶參數(shù)的查詢操作
@Query
注解還可以用于執(zhí)行帶參數(shù)的查詢操作。例如:
@Dao interface UserDao { @Query("SELECT * FROM users WHERE id = :id") fun getUserById(id: Int): User }
在這個例子中,我們使用 @Query
注解定義了一個帶有參數(shù)的 SQL 查詢語句,該語句將返回 users
表中 id
字段等于給定值的數(shù)據(jù)。我們將此查詢定義為 getUserById()
方法的一部分,并將 id
參數(shù)傳遞給查詢語句。
(3)使用關(guān)聯(lián)查詢
最后,@Query
注解還可以用于執(zhí)行關(guān)聯(lián)查詢。關(guān)聯(lián)查詢是一種可以跨多個表查詢數(shù)據(jù)的查詢類型。
@Dao interface UserDao { @Query("SELECT * FROM users INNER JOIN addresses ON users.address_id = addresses.id") fun getUsersWithAddresses(): List<UserWithAddress> }
在這個例子中,我們使用 @Query
注解定義了一個關(guān)聯(lián)查詢語句,該語句將返回 users
表中的數(shù)據(jù)以及與之關(guān)聯(lián)的 addresses
表中的數(shù)據(jù)。我們將此查詢定義為 getUsersWithAddresses()
方法的一部分,并使用 INNER JOIN
子句指定 users
表和 addresses
表之間的關(guān)系。
14、@Insert、@Update、@Delete
顧名思義哈,也就不用舉例了,嘻嘻嘻
@Dao interface UserDao { @Insert\@Update\@Delete fun xxxUser(user: User) }
15、多數(shù)據(jù)源
使用Kotlin Flow可以很方便地處理多個數(shù)據(jù)源的情況。在使用Room時,我們可以在Repository層中實現(xiàn)本地和遠(yuǎn)程數(shù)據(jù)源的邏輯,并使用Kotlin Flow來組合和轉(zhuǎn)換數(shù)據(jù)。
以下是一個示例,演示了如何使用Room和Kotlin Flow處理多個數(shù)據(jù)源的情況:
class UserRepository( private val userDao: UserDao, private val api: ApiService ) { fun getUsers(): Flow<List<User>> { val localUsers = userDao.getAll().asFlow() val remoteUsers = api.getUsers().asFlow() return localUsers.combine(remoteUsers) { local, remote -> // 合并本地和遠(yuǎn)程數(shù)據(jù) (local + remote).distinctBy { it.id } } } suspend fun updateUser(user: User) { api.updateUser(user.id, user) userDao.update(user) } }
在以上示例中,我們在UserRepository中使用了本地和遠(yuǎn)程數(shù)據(jù)源,并使用Kotlin Flow.combine操作符將本地和遠(yuǎn)程數(shù)據(jù)源合并在一起,并在最后返回一個Flow對象。我們還使用了suspend修飾符將updateUser方法標(biāo)記為掛起函數(shù),以便可以在協(xié)程中執(zhí)行異步操作。
很方便吧
16、@Fts3和@Fts4
這個一般用不到來著,不過如果你要做小說軟件的話,可能有用。用于創(chuàng)建全文本搜索虛擬表。全文本搜索是一種在大型文本數(shù)據(jù)集中搜索特定文本片段的技術(shù)。當(dāng)您需要在應(yīng)用程序中實現(xiàn)全文本搜索時,可以使用這兩個注解來創(chuàng)建虛擬表。
(1)@Fts3 注解
@Fts3
注解用于創(chuàng)建一個基于 SQLite FTS3 算法的虛擬表。例如:
@Fts3 @Entity(tableName = "books") data class Book( @ColumnInfo(name = "book_title") val title: String, @ColumnInfo(name = "book_author") val author: String, @ColumnInfo(name = "book_description") val description: String )
在這個例子中,我們使用 @Fts3
注解定義了一個名為 books
的虛擬表。該表將基于 title
、author
和 description
列的內(nèi)容創(chuàng)建一個全文本索引。當(dāng)您執(zhí)行全文本搜索時,將使用該索引來查找與搜索查詢匹配的行。
(2)@Fts4 注解
@Fts4
注解用于創(chuàng)建一個基于 SQLite FTS4 算法的虛擬表。例如:
@Fts4(contentEntity = Book::class) @Entity(tableName = "book_fts") data class BookFts( @ColumnInfo(name = "book_fts_title") val title: String, @ColumnInfo(name = "book_fts_author") val author: String, @ColumnInfo(name = "book_fts_description") val description: String )
在這個例子中,我們使用 @Fts4
注解定義了一個名為 book_fts
的虛擬表。該表將基于 title
、author
和 description
列的內(nèi)容創(chuàng)建一個全文本索引。與 @Fts3
注解不同的是,@Fts4
注解需要使用 contentEntity
參數(shù)指定要創(chuàng)建索引的實體類。
(3)使用全文本搜索
創(chuàng)建全文本搜索虛擬表后,您可以使用 Room 中的 MATCH
關(guān)鍵字來執(zhí)行全文本搜索。例如:
@Dao interface BookDao { @Query("SELECT * FROM books WHERE books MATCH :query") fun searchBooks(query: String): List<Book> }
在這個例子中,我們使用 MATCH
關(guān)鍵字來執(zhí)行全文本搜索操作。該操作將在 books
虛擬表中搜索與 query
參數(shù)匹配的行,并返回所有匹配的結(jié)果。注意,在使用這些注解時,請確保為要搜索的列創(chuàng)建了索引,以避免搜索操作變得緩慢或不可用。
總結(jié)
我們在這一篇提到了17個注釋,,我們可以使用以上注解來定義實體類、DAO接口和數(shù)據(jù)庫,并實現(xiàn)各種數(shù)據(jù)操作和類型轉(zhuǎn)換。這些注解可以幫助我們更好地使用Room進(jìn)行開發(fā),并實現(xiàn)更加復(fù)雜和高效的功能,涵蓋了絕大數(shù)開發(fā)的需求。當(dāng)然,在實際開發(fā)中,我們還需要根據(jù)具體的業(yè)務(wù)需求和技術(shù)特點來選擇合適的方案和實現(xiàn)方式。
實際上,Room的應(yīng)用遠(yuǎn)不止如此,如果有人感興趣的話,我就繼續(xù)出下一期吧!
感謝
- 校稿:ChatGpt/Bing
- 文筆優(yōu)化:ChatGpt/Bing/秘塔寫作貓
以上就是快速了解Android Room使用細(xì)則進(jìn)階的詳細(xì)內(nèi)容,更多關(guān)于Android Room使用細(xì)則的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android關(guān)于按鈕點擊效果實現(xiàn)的方法
今天小編就為大家分享一篇關(guān)于android關(guān)于按鈕點擊效果實現(xiàn)的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03Android中AlertDialog的六種創(chuàng)建方式
這篇文章主要介紹了Android中AlertDialog的六種創(chuàng)建方式的相關(guān)資料,需要的朋友可以參考下2016-07-07Android Parcelable與Serializable詳解及區(qū)別
這篇文章主要介紹了Android Parcelable與Serializable詳解及區(qū)別的相關(guān)資料,需要的朋友可以參考下2017-01-01Android MotionEvent中g(shù)etX()和getRawX()的區(qū)別實例詳解
這篇文章主要介紹了Android MotionEvent中g(shù)etX()和getRawX()的區(qū)別實例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03