快速了解Android Room使用细则进阶

2、@ForeignKey和@PrimaryKey

考验你数据库知识的时候来了!因为你会频繁看到@PrimaryKey所以先讲它

@ForeignKey 注解用于定义外键关系,它指定了一个实体类中的一个字段是另一个实体类的主键。这种关系被称为“外键关系”,并且可以用于在多个表之间建立关联。

例如,如果有两个实体类 User 和 Address,并且想要将它们关联起来,则可以使用 @ForeignKey 注解来指定 Address 中的 user_id 字段是 User 的主键:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@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 的主键。这将创建一个外键关系,确保在插入或更新 Address 表中的数据时,user_id 字段的值必须是 User 表中存在的主键值之一。

@PrimaryKey 注解用于指定实体类中的一个字段是主键。主键是用于唯一标识每个实体类对象的字段。在 Room 中,每个实体类必须至少有一个字段被指定为主键。

例如,如果有一个实体类 User,并且想要将 id 字段指定为主键,则可以使用 @PrimaryKey 注解:

1

2

3

4

5

@Entity(tableName = "users")

data class User(

    @PrimaryKey val id: Int,

    val name: String

)

在这个例子中,我们使用 @PrimaryKey 注解将 id 字段指定为 User 实体类的主键。这将确保在插入或更新 User 表中的数据时,每个 id 字段的值都是唯一的。

3、@TypeConverters

在使用Room时,你可能会遇到需要在Entity类中使用非基本类型的情况,例如Date、Calendar、List等类型。在这种情况下,你可以使用TypeConverters将这些类型转换为Room可以存储的类型。在Room中,可以使用@TypeConverter注解来定义一个类型转换器,例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

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 ,不同的位置造成的影响也是不同的,实际上可以应用到以下四个地方:

  • 实体类:在 @Entity 注解中使用,可以在处理该实体类时使用它们。
  • DAO 接口:在 DAO 接口中使用,可以在执行该 DAO 中的查询时使用它们。
  • 数据库类:在 RoomDatabase 类中使用, 可以在整个数据库中使用它们。
  • 实体类中的属性:在实体类中的属性,可以在处理该属性时使用指定的类型转换器

4、@Relation

@Relation 用于在实体类之间建立关系。它可以用于定义两个或更多实体之间的关系,这些实体可以在数据库中分别存储在不同的表中。

@Relation 注解应该与 @Query 注解一起使用,以便 Room 可以在查询结果中返回相关实体之间的关系。@Relation 注解的一个常见用例是定义父子关系,其中一个实体包含对另一个实体的引用。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

@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,它们之间有一个父子关系,其中一个用户可以拥有多本书。然后,我们定义了一个 UserWithBooks 数据类,它包含一个嵌套的 User 实体和一个 @Relation 注解,用于指定如何检索与该用户关联的所有书籍。@Relation 注解包括 parentColumn 和 entityColumn 参数,分别指定父实体的主键列和子实体的外键列。

当我们使用 @Relation 注解时,我们需要在查询中使用 SELECT 语句,以便 Room 可以检索相关的实体。例如,在 Dao 接口中,我们可以使用以下查询:

1

2

3

4

//Transaction下一点就会说

@Transaction

@Query("SELECT * FROM users WHERE id = :id")

fun getUserWithBooks(id: Int): UserWithBooks

此外,我们使用 SELECT * 语句来检索所有用户属性和相关的书籍列表,因为 UserWithBooks 数据类包含一个嵌套的 User 实体和一个 List<Book> 列表。

5、@Transaction

第4点说到@Relation时使用到了@Transaction。在这个查询中,我们使用 @Transaction 注解来确保整个查询作为一个事务执行,以便 Room 可以在单个操作中检索 User 实体和与之相关的所有 Book 实体。

@Transaction用于将一组数据库操作包装在一个事务中。它可以确保在执行数据库操作时保持数据库的一致性,并在必要时回滚事务以确保数据的完整性。

在 Room 中,单个数据库操作(例如插入、更新或删除)是自动运行在事务中的。但是,当需要执行多个数据库操作时,可能需要手动创建一个事务来确保这些操作原子性地执行。如果需要执行多个数据库操作,请始终考虑使用 @Transaction 注解。这可以避免数据不一致和其他与数据库操作相关的问题。

6、@Embedded

上一篇,有同志说@Embedded很好用,的确如此哈

@Embedded用于指定一个实体类中的一个或多个字段应该作为其所属的另一个实体的嵌入式对象。这使得 Room 可以将多个相关实体的数据组合成一个单独的对象,从而简化了数据库操作。

当在一个实体类中使用 @Embedded 注解时,可以指定该实体类中的一个或多个字段应该嵌入到另一个实体类中。例如,假设有一个 Address 实体类和一个 User 实体类,其中 User 实体类包含一个 Address 对象。可以使用 @Embedded 注解将 Address 对象嵌入到 User 实体类中:

1

2

3

4

5

6

7

8

9

10

11

12

@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 注解指定了该对象应该嵌入到 User 实体类中。在查询数据库时,Room 将自动组合 User 实体类和 Address 实体类中的字段,以便可以轻松地访问和操作它们。

还可以使用 prefix 参数来指定 Room 应该在组合两个实体类中的字段时使用的前缀。例如:

1

2

3

4

5

6

7

@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 可以区分两个地址对象中的相同字段,并将它们组合成一个单独的对象。

当然你也可以这么干

1

2

3

4

5

6

@Entity(tableName

data class User(

    @PrimaryKey val id: Int,

    val name: String,

    @Embedded @ColumnInfo(name = "home_address") val homeAddress: Address

)

7、@ColumnInfo

可以看到,我们刚刚用到了@ColumnInfo这个注解,用于自定义实体类中的列名、默认值和其他属性。当需要将一个实体类映射到数据库表时,可以使用 @ColumnInfo 注解来指定实体类中的每个字段在数据库表中的名称和其他属性。

(1)指定实体类中的字段名称

@ColumnInfo 注解最常用的用途是指定实体类中的字段名称。例如:

1

2

3

4

5

6

@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。这意味着在数据库表中,这个字段将被命名为 full_name,而不是 name

(2)指定实体类中的字段默认值

@ColumnInfo 注解还可以用于指定实体类中的字段默认值。例如:

1

2

3

4

5

6

@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,并将其默认值设置为 true。这意味着在数据库表中,这个字段将被命名为 is_active,并且默认值将为 true

(3)指定实体类中的字段约束

@ColumnInfo 注解还可以用于指定实体类中的字段约束。例如:

1

2

3

4

5

6

7

@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,并将其默认值设置为 CURRENT_TIMESTAMP。这意味着在数据库表中,这个字段将被命名为 created_at,并且默认值将为 CURRENT_TIMESTAMP

8、@Ignore

很直观的注解哈。指定实体类中应该忽略的字段。当需要在实体类中添加一个字段,但不想将其映射到数据库表中时,可以使用 @Ignore 注解来指定该字段应该被忽略。

忽略一个实体类中的字段

@Ignore 注解最常用的用法是忽略一个实体类中的字段,从而防止该字段被映射到数据库表中。例如:

1

2

3

4

5

6

@Entity(tableName = "users")

data class User(

    @PrimaryKey val id: Int,

    val name: String,

    @Ignore val password: String

)

在这个例子中,我们使用 @Ignore 将 password 字段指定为应该被忽略的字段。这意味着在数据库表中,这个字段将不会出现,也不会被映射到 User 实体类中。

(1)忽略一个实体类中的 getter 和 setter 方法

除了忽略一个实体类中的字段外,@Ignore 注解还可以用于忽略一个实体类中的 getter 和 setter 方法。这可以帮助控制 Room 如何处理实体类中的方法。

例如,如果希望 Room 不生成一个实体类中的 setter 方法,则可以将 @Ignore 注解添加到该方法上:

1

2

3

4

5

6

7

8

9

10

11

@Entity(tableName = "users")

data class User(

    @PrimaryKey val id: Int,

    val name: String,

    val password: String

) {

    @Ignore

    fun setPassword(password: String) {

        // ...

    }

}

在这个例子中,我们使用 @Ignore 将 setPassword 方法指定为应该被忽略的方法。这意味着 Room 不会生成一个 setter 方法来设置 password 字段的值。

(2)忽略一个实体类中的整个构造函数

最后,@Ignore 注解还可以用于忽略一个实体类中的整个构造函数。这可以帮助控制 Room 如何处理实体类中的构造函数。

例如,如果希望 Room 不使用默认的构造函数来创建实体类的实例,则可以使用 @Ignore 注解标记该构造函数:

1

2

3

4

5

6

7

8

9

@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 将第二个构造函数指定为应该被忽略的构造函数。这意味着 Room 不会使用这个构造函数来创建 User 实体类的实例。

9、@Index

考验你数据库知识的时候来了!索引(个索引、多个索引、复合索引)可以提高数据库表查询的性能,因为它们使数据库系统能够更快地查找和排序表中的数据。

(1)在一个实体类中创建单个索引

@Index 注解最常用的用法是在一个实体类中创建单个索引。例如:

1

2

3

4

5

6

@Entity(tableName = "users", indices = [Index(value = ["name"])])

data class User(

    @PrimaryKey val id: Int,

    val name: String,

    val age: Int

)

在这个例子中,我们使用 @Index 注解在 name 字段上创建了一个单个索引。这将使数据库系统能够更快地查找和排序 User 表中的数据。

(2)在一个实体类中创建多个索引

除了在一个实体类中创建单个索引外,@Index 注解还可以用于在一个实体类中创建多个索引。例如:

1

2

3

4

5

6

7

8

9

@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 字段上创建了两个索引。这将使数据库系统能够更快地查找和排序 User 表中的数据。

(3)在一个实体类中创建复合索引

@Index 注解还可以用于在一个实体类中创建复合索引。复合索引是指将多个字段组合在一起以创建一个索引,这将使数据库系统能够更快地查找和排序这些字段的组合。

例如,如果希望在 User 表中按照 name 和 age 字段的组合进行排序,则可以使用 @Index 注解来创建一个复合索引:

1

2

3

4

5

6

7

8

@Entity(tableName = "users", indices = [

    Index(value = ["name", "age"])

])

data class User(

    @PrimaryKey val id: Int,

    val name: String,

    val age: Int

)

在这个例子中,我们使用 @Index 注解在 name 和 age 字段上创建了一个复合索引。这将使数据库系统能够更快地查找和排序 User 表中按照 name 和 age 字段的组合进行排序的数据。

10、@Entity

当在 Room 中定义一个实体类时,必须使用 @Entity 注解来指定该类应该被映射到数据库中的哪个表。

(1)在一个实体类中指定表名

@Entity 注解最常用的用法是在一个实体类中指定表名。例如:

1

2

3

4

5

6

@Entity(tableName = "users")

data class User(

    @PrimaryKey val id: Int,

    val name: String,

    val age: Int

)

在这个例子中,我们使用 @Entity 注解将 User 实体类映射到名为 users 的数据库表中。这将使 Room 能够将 User 类中的字段映射到数据库表中的相应列中。

(2)在一个实体类中指定索引

除了在一个实体类中指定表名外,@Entity 注解还可以用于在一个实体类中指定索引。索引可以提高数据库表查询的性能,因为它们使数据库系统能够更快地查找和排序表中的数据。

例如,如果希望在 User 表中按照 name 字段进行排序,则可以使用 @Entity 注解来创建一个索引:

1

2

3

4

5

6

@Entity(tableName = "users", indices = [Index(value = ["name"])])

data class User(

    @PrimaryKey val id: Int,

    val name: String,

    val age: Int

)

在这个例子中,我们使用 @Entity 注解在 name 字段上创建了一个索引。这将使数据库系统能够更快地查找和排序 User 表中的数据。

(3)在一个实体类中指定继承关系

最后,@Entity 注解还可以用于在一个实体类中指定继承关系。如果的实体类继承自另一个实体类,则可以使用 @Entity 注解来指定它们之间的关系。

例如,如果有一个 Person 实体类和一个 Employee 实体类,Employee 实体类继承自 Person 实体类,则可以使用 @Entity 注解来指定它们之间的关系:

1

2

3

4

5

6

7

8

9

10

11

@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 的数据库表中,并将 Employee 实体类映射到名为 employees 的数据库表中。此外,我们还使用 @Entity 注解指定了 Employee 实体类继承自 Person 实体类,并使用 @ColumnInfo 注解将 person_id 字段指定为 Employee 表中的外键。这将确保在插入或更新 Employee 表中的数据时,person_id 字段的值必须是 Person 表中存在的主键值之一。

11、@Dao

@Dao 是用于访问数据库中数据的一种抽象层。在 Room 中,每个 DAO 都定义了一组用于与数据库进行交互的方法。意思是就这么用,没啦。

1

2

@Dao

interface UserDao {}

12、@Database

@Database 注解是 Room 中的一个注解,用于定义数据库类。当在 Room 中定义一个数据库时,必须使用 @Database 注解来指定该数据库包含哪些实体类和版本号等信息。

(1)在一个类中定义数据库实例

@Database 注解最常用的用法是在一个类中定义数据库实例。例如:

1

2

3

4

@Database(entities = [User::class], version = 1)

abstract class AppDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

}

在这个例子中,我们使用 @Database 注解定义了一个数据库类 AppDatabase,并在其中指定了包含 User 实体类的数据库版本号。此外,我们还定义了一个抽象方法 userDao(),用于返回一个 UserDao 数据访问对象 (DAO)。

(2)指定多个实体类

@Database 注解还可以用于指定多个实体类。例如:

1

2

3

4

5

@Database(entities = [User::class, Address::class], version = 1)

abstract class AppDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    abstract fun addressDao(): AddressDao

}

在这个例子中,我们使用 @Database 注解指定了包含 User 和 Address 实体类的数据库版本号。然后,我们定义了两个抽象方法 userDao() 和 addressDao(),分别用于返回 UserDao 和 AddressDao 数据访问对象 (DAO)。

(3)指定数据库升级策略

最后,@Database 注解还可以用于指定数据库升级策略。当升级数据库时,可能需要指定一些操作来处理数据模式的变化。Room 提供了两种升级策略:Migrate 和 FallbackToDestructiveMigration

例如,如果希望在升级数据库时保留现有数据,可以使用 Migrate 升级策略:

1

2

3

4

5

6

7

8

9

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 升级策略将数据库版本从 1 升级到 2。我们定义了一个名为 migration1to2 的迁移对象,用于在升级数据库时执行自定义的 SQL 语句。然后,我们使用 @Database 注解指定了包含 User 实体类的数据库版本号和升级策略。

如果不需要保留现有数据,可以使用 FallbackToDestructiveMigration 升级策略:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@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 升级策略将数据库版本从 1 升级到 2。我们使用 @Database 注解指定了包含 User 实体类的数据库版本号和升级策略,并将 exportSchema 参数设置为 false,以避免生成不必要的 JSON 文件。

13、@Query

用于定义SQL查询语句,可以在之前的例子中找到,许多的@Query的身影 (考验你数据库基础的时候到了!)

(1)基本查询操作

@Query 注解最常用的用法是执行基本的查询操作。例如:

1

2

3

4

5

@Dao

interface UserDao {

    @Query("SELECT * FROM users")

    fun getAllUsers(): List<User>

}

在这个例子中,我们使用 @Query 注解定义了一个基本的 SQL 查询语句,该语句将返回 users 表中的所有数据。我们将此查询定义为 getAllUsers() 方法的一部分,以便在需要时调用该方法。

(2)带参数的查询操作

@Query 注解还可以用于执行带参数的查询操作。例如:

1

2

3

4

5

@Dao

interface UserDao {

    @Query("SELECT * FROM users WHERE id = :id")

    fun getUserById(id: Int): User

}

在这个例子中,我们使用 @Query 注解定义了一个带有参数的 SQL 查询语句,该语句将返回 users 表中 id 字段等于给定值的数据。我们将此查询定义为 getUserById() 方法的一部分,并将 id 参数传递给查询语句。

(3)使用关联查询

最后,@Query 注解还可以用于执行关联查询。关联查询是一种可以跨多个表查询数据的查询类型。

1

2

3

4

5

@Dao

interface UserDao {

    @Query("SELECT * FROM users INNER JOIN addresses ON users.address_id = addresses.id")

    fun getUsersWithAddresses(): List<UserWithAddress>

}

在这个例子中,我们使用 @Query 注解定义了一个关联查询语句,该语句将返回 users 表中的数据以及与之关联的 addresses 表中的数据。我们将此查询定义为 getUsersWithAddresses() 方法的一部分,并使用 INNER JOIN 子句指定 users 表和 addresses 表之间的关系。

14、@Insert、@Update、@Delete

顾名思义哈,也就不用举例了,嘻嘻嘻

1

2

3

4

5

@Dao

interface UserDao {

    @Insert\@Update\@Delete

    fun xxxUser(user: User)

}

15、多数据源

使用Kotlin Flow可以很方便地处理多个数据源的情况。在使用Room时,我们可以在Repository层中实现本地和远程数据源的逻辑,并使用Kotlin Flow来组合和转换数据。

以下是一个示例,演示了如何使用Room和Kotlin Flow处理多个数据源的情况:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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 ->

            // 合并本地和远程数据

            (local + remote).distinctBy { it.id }

        }

    }

    suspend fun updateUser(user: User) {

        api.updateUser(user.id, user)

        userDao.update(user)

    }

}

在以上示例中,我们在UserRepository中使用了本地和远程数据源,并使用Kotlin Flow.combine操作符将本地和远程数据源合并在一起,并在最后返回一个Flow对象。我们还使用了suspend修饰符将updateUser方法标记为挂起函数,以便可以在协程中执行异步操作。

很方便吧

16、@Fts3和@Fts4

这个一般用不到来着,不过如果你要做小说软件的话,可能有用。用于创建全文本搜索虚拟表。全文本搜索是一种在大型文本数据集中搜索特定文本片段的技术。当您需要在应用程序中实现全文本搜索时,可以使用这两个注解来创建虚拟表。

(1)@Fts3 注解

@Fts3 注解用于创建一个基于 SQLite FTS3 算法的虚拟表。例如:

1

2

3

4

5

6

7

@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 的虚拟表。该表将基于 titleauthor 和 description 列的内容创建一个全文本索引。当您执行全文本搜索时,将使用该索引来查找与搜索查询匹配的行。

(2)@Fts4 注解

@Fts4 注解用于创建一个基于 SQLite FTS4 算法的虚拟表。例如:

1

2

3

4

5

6

7

@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 的虚拟表。该表将基于 titleauthor 和 description 列的内容创建一个全文本索引。与 @Fts3 注解不同的是,@Fts4 注解需要使用 contentEntity 参数指定要创建索引的实体类。

(3)使用全文本搜索

创建全文本搜索虚拟表后,您可以使用 Room 中的 MATCH 关键字来执行全文本搜索。例如:

1

2

3

4

5

@Dao

interface BookDao {

    @Query("SELECT * FROM books WHERE books MATCH :query")

    fun searchBooks(query: String): List<Book>

}

在这个例子中,我们使用 MATCH 关键字来执行全文本搜索操作。该操作将在 books 虚拟表中搜索与 query 参数匹配的行,并返回所有匹配的结果。注意,在使用这些注解时,请确保为要搜索的列创建了索引,以避免搜索操作变得缓慢或不可用。

猜你喜欢

转载自blog.csdn.net/sinat_40572875/article/details/129303492