android 我有办法在房间数据库中搜索对象吗

ecbunoof  于 2023-01-15  发布在  Android
关注(0)|答案(1)|浏览(113)

我现在正在构建一个带有本地数据库的Android应用程序
表结构如下所示(来自API)

@Entity
data class Person(
 name: Name,
 ... ... ...
 ... ... ...
)
data class Name(
 legalName: String.
 common: String
)

这是SQL代码,我试过用法律的的名字

@Query("SELECT * FROM person WHERE name.legalName = :legalName")
suspend fun getPersonByName (legalName: String): Person?

这给了我编译错误,因为我们不能按名称搜索。legalName在房间数据库。此外,我们有静态名单的人(只有法律名称)在主页(没有ID或其他合理的字段来执行搜索)
我们是否有适当的方法来搜索带有legalName字段的用户?

tvokkenx

tvokkenx1#

Room使用@Entity注解来确定底层SQLite表模式。这样注解的类是一个对象,但对象的各个字段/成员存储为表中的列,而不是对象。
此类列只能是以下特定类型:-

  • 整数类型值(例如Int、Long.... Boolean)(INTEGER列类型)
  • 字符串类型值(例如,字符串)(列类型为TEXT)
  • 十进制/浮点型值(例如,浮点型、双精度型)(列类型真实的)
  • 字节流类型值(例如ByteArray)(列类型BLOB)
  • null(列定义不能有NOT NULL约束条件)

因此,对象不能直接存储或存储SQLite没有对象的概念/理解,只是将列分组到表中。
在您的情况下,name字段是一个Name对象,而Room将需要2个类型转换器:-

  • 将对象转换为可以表示对象的上述之一(通常是对象的JSON表示)
  • 另一个用于将存储的数据转换回Object。
  • 这允许在单列中表示对象。

因此,要查询对象的字段/成员,您需要考虑如何相应地表示和搜索它。
不会有name.legalName列,只有name列,并且表示取决于TypConverter,搜索(WHERE子句)也是如此。
现在根据您的代码考虑以下内容:

@Entity
data class Person(
    @PrimaryKey
    var id: Long?=null,
    var name: Name,
    @Embedded /* Alternative */
    var otherName: Name
)
data class Name(
    var legalName: String,
    var common: String
)
  • 根据房间要求添加主键
  • @Embedded作为复制字段/成员(legalName和common as字段)的替代方法

因此,name列将需要类型转换器,因为每个类的2个注解t中的每一个都带有@TypeConverter(注意单数),必须定义定义类型转换器的类(参见下面的***TheDatabase***类)。因此:-

class TheTypeConverters {
    /* Using Library as per dependency implementation 'com.google.code.gson:gson:2.10.1' */
    @TypeConverter
    fun convertFromNameToJSONString(name: Name): String = Gson().toJson(name)
    @TypeConverter
    fun convertFromJSONStringToName(jsonString: String): Name = Gson().fromJson(jsonString,Name::class.java)
}
  • 注意,还有其它的Gson库可以提供更好的功能。

实体(在本例中只有一个)必须在@Database注解中定义,用于扩展RoomDatabase()的抽象类。所以:-

@TypeConverters(value = [TheTypeConverters::class])
@Database(entities = [Person::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getTheDAOs(): TheDAOs

    companion object {
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
                    .allowMainThreadQueries() /* For brevity convenience of the demo */
                    .build()
            }
            return  instance as TheDatabase
        }
    }
}
  • @TypeConverters注解(复数)除了定义TypeConverter所在的一个或多个类之外,还定义了作用域 @Database是最大的作用域)

在这个阶段,可以编译项目(CTRL + F9),注解处理将生成一些代码。重要的是TheDatabase_Impl在java中(生成)* 名称与@Database注解类相同,后缀为_Impl*。这包括一个方法createAllTables,它是创建SQLite表时使用的SQL。person表的SQL是:-

CREATE TABLE IF NOT EXISTS `Person` (
    `id` INTEGER, 
    `name` TEXT NOT NULL, 
    `legalName` TEXT NOT NULL, 
    `common` TEXT NOT NULL, PRIMARY KEY(`id`)
)

可以看出,id列是主键,name列是name对象的转换表示,然后是法律的和common列,因为name对象通过otherName字段@Embedded。
最后用下面的@Dao注解接口(允许添加一些数据)来完成:

@Dao
interface TheDAOs {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(person: Person): Long

    @Query("SELECT * FROM person")
    fun getAllPersonRows(): List<Person>
}

主要活动为:-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: TheDAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getTheDAOs()
        dao.insert(Person(null, name =  Name("Frederick Bloggs","Fred Bloggs"), otherName = Name("Frederick ","Fred Bloggs")))
        dao.insert(Person(null, name =  Name("Jane Doe","Jane Doe"), otherName = Name("Jane Doe","Jane Doe")))
    }
}

项目运行,然后使用App Inspection查看实际数据库,然后:-

名称列包含字符串{"common":"Fred Bloggs","legalName":"Frederick Bloggs"}**
因此,用于查找以Fred开头的所有法律的名称的WHERE子句可以是
WHERE instr(name,',\"legalName\":\"Fred')WHERE name LIKE '%,\"legalName\":\"Fred%'

  • 应当注意,由于在列内的搜索,两者都需要全扫描。
  • 当然,这假设不存在具有公共名称,"legalName":"Fred的名称,或者作为公共名称的一部分或整个字符串的某个其他部分的名称,即,可能难以预测将来可能是什么结果。

对于替代@EmbeddedName对象,legalNamecommon列更容易搜索,以Fred开头的合法名称的等效搜索可以是

WHERE legalname LIKE 'Fred%'

Fred不可能出现在满足条件的其他地方。搜索只是针对单个列/值,没有其他内容。索引列很可能会提高效率。
@Dao注解接口DAO修改为:-

@Dao
interface TheDAOs {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(person: Person): Long

    @Query("SELECT * FROM person WHERE instr(name,',\"legalName\":\"Fred')")
    fun getPersonsAccordingToLegalNameInNameObject(): List<Person>

    @Query("SELECT * FROM person WHERE legalName LIKE 'Fred%'")
    fun getPersonsAccordingToLegalName(): List<Person>
}

主要活动为:-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: TheDAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getTheDAOs()
        dao.insert(Person(null, name =  Name("Frederick Bloggs","Fred Bloggs"), otherName = Name("Frederick ","Fred Bloggs")))
        dao.insert(Person(null, name =  Name("Jane Doe","Jane Doe"), otherName = Name("Jane Doe","Jane Doe")))
        logPersonList(dao.getPersonsAccordingToLegalNameInNameObject(),"RUN1")
        logPersonList(dao.getPersonsAccordingToLegalName(),"RUN2")
    }

    private fun logPersonList(personList: List<Person>, suffix: String) {
        for (p in personList) {
            Log.d("DBINFO_${suffix}","Person ID is ${p.id} Name.legalName is ${p.name.legalName} Name.common is ${p.name.common} LegalName is ${p.otherName.legalName} Common is ${p.otherName.common}")
        }
    }
}

然后运行(安装后第一次)日志,日志包含:-

2023-01-14 11:26:03.738 D/DBINFO_RUN1: Person ID is 1 Name.legalName is Frederick Bloggs Name.common is Fred Bloggs LegalName is Frederick  Common is Fred Bloggs
2023-01-14 11:26:03.740 D/DBINFO_RUN2: Person ID is 1 Name.legalName is Frederick Bloggs Name.common is Fred Bloggs LegalName is Frederick  Common is Fred Bloggs

即,在该有限演示中,预期结果为任一方式。

  • 请注意,Name.legalNameName.common不是访问数据的方式,它只是用于轻松区分相似值的文本。

相关问题