具有一对一关系的房间数据库,如地址、城市和州

Room database with one-to-one relation like Address, City and State

我在 android 文档中查找了我的问题的答案,但找不到。要使用这些 类 中包含的信息创建 recyclerview,我如何在 Room

中获取此信息的列表
@Entity(
    foreignKeys = [
        ForeignKey(
            entity = City::class,
            parentColumns = arrayOf("id"),
            childColumns = arrayOf("cityfk"),
            onDelete = ForeignKey.NO_ACTION
        )
    ]
)
data class Address(
    @PrimaryKey
    @ColumnInfo
    var id: Long = 0
) : Serializable {

    @ColumnInfo
    var name: String = ""

    @ColumnInfo(index = true)
    var cityfk: Long = 0

}

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = State::class,
            parentColumns = arrayOf("id"),
            childColumns = arrayOf("statefk"),
            onDelete = ForeignKey.NO_ACTION
        )
    ]
)
data class City(
    @PrimaryKey
    @ColumnInfo
    var id: Long = 0
) : Serializable {

    @ColumnInfo
    var name: String = ""

    @ColumnInfo(index = true)
    var statefk: Long = 0
}

@Entity
data class State(
    @PrimaryKey
    @ColumnInfo
    var id: Long = 0
) : Serializable {

    @ColumnInfo
    var name: String = ""

}

如何获得列出 类 的地址列表?

如何在 ANSI 中得​​到这样的结果 SQL:

select     ADDRESS.NAME ADDRESS
         , CITY.NAME CITY
         , STATE.NAME STATE

from       ADDRESS

join       CITY
on         CITY.ID = ADDRES.CITYFK

join       STATE
on         STATE.ID = CITY.STATEFK

您通常会有一个 POJO 来表示组合数据。然后,您可以为提取的列设置 field/variable,指出值与喜欢的命名变量匹配。

您可以使用@Embedded 来完整地包含一个实体,因此理论上嵌入地址城市和州。

  • 请参阅 variable/column 名称问题

您可以将@Embedded 与@Relation 一起用于child(children),但不能用于grandchildren(例如State)。您需要一个带有 State POJO 的底层 City,其中嵌入了 City 并且 State 与 @Relation 相关。

  • variable/column 使用@Relation 时名称不是问题,因为房间构建来自 parent.
  • 的基础查询

Variable/Column 名字问题

Room 根据变量名将列映射到变量。因此,如果对所有三个实体使用更简单的 @Embedded,idname 列将会出现问题。

  • 我建议始终使用唯一名称,例如addressId、cityId、StateId,(至少对于列名,例如 @ColumnInfo(name = "addressId"))但更简单的是只使用 var addressid.

  • 另一种方法是在某些上使用@Embedded(prefix = "the_prefix"),这告诉 room 将变量与带有前缀的列名匹配,因此您需要使用如 SQL。显然 the_prefix 将被更改为适合。

道家

如果将@Embedded 与@Relation 一起使用,那么您只需要获取 parent so

@Query("SELECT * FROM address")
fun getAddressWithCityAndWithState(): List<AddressWithCityAndWithState>
  • 其中 AddressWithCityAndWithState 是具有地址 @Embedded 和 CityWithState 与 @Relation 的 POJO。

您还需要带有 City @Embedded 和 State 和 @Relation 的随附 CityWithState POJO。

如果嵌入地址、城市和州,其中城市前缀为“city_”,州前缀为“state_”,那么您可以使用类似 :-

@Query("SELECT address.*, city.id AS city_id, city.name AS city_name, state.id AS state_id, state.name AS state_name FROM address JOIN city ON address.cityfk = city.it JOIN state ON city.statefk = state.id")
fun getAddressWithCityAndWithState(): List<AddressWithCityAndWithState>
  • 其中 AddressWithCityAndWithState 是具有地址、城市和州的 POJO @Embedded

以上为in-principle.

工作示例

下面是一个基于

的工作示例
  • a) 重命名列以避免歧义并且
  • b) 在 POJO AddressWithCityWithState
  • 中使用所有三个 类 的 @Embedded

首先更改地址、城市和州以重命名列:-

地址:-

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = City::class,
            parentColumns = arrayOf("city_id"), //<<<<<<<<<< CHANGED
            childColumns = arrayOf("cityfk"),
            onDelete = ForeignKey.NO_ACTION
        )
    ]
)
data class Address(
    @PrimaryKey
    @ColumnInfo(name ="address_id") //<<<<<<<<<< ADDED name
    var id: Long = 0
) : Serializable {

    @ColumnInfo(name = "address_name") //<<<<<<<<<< ADDDED name
    var name: String = ""

    @ColumnInfo(index = true)
    var cityfk: Long = 0
}

城市:-

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = State::class,
            parentColumns = arrayOf("state_id"), //<<<<<<<<<< changed
            childColumns = arrayOf("statefk"),
            onDelete = ForeignKey.NO_ACTION
        )
    ]
)
data class City(
    @PrimaryKey
    @ColumnInfo(name = "city_id") // <<<<<<<<<< ADDED name
    var id: Long = 0
) : Serializable {

    @ColumnInfo(name = "city_name") //<<<<<<<<<< ADDED name
    var name: String = ""

    @ColumnInfo(index = true)
    var statefk: Long = 0
}

状态:-

@Entity
data class State(
    @PrimaryKey
    @ColumnInfo(name = "state_id") // ADDED name
    var id: Long = 0
) : Serializable {

    @ColumnInfo(name = "state_name") // ADDED name
    var name: String = ""
}

下一个 POJO AddressWithCityWithState :-

data class AddressWithCityWithState (
    @Embedded
    val address: Address,
    @Embedded
    val city: City,
    @Embedded
    val state: State
)
  • 由于列名唯一,不需要 prefix = ?

一个合适的DAO :-

@Query("SELECT * FROM address JOIN city on address.cityfk = city.city_id JOIN state ON city.statefk = state.state_id")
    fun getAllAddressesWithCityAndWithState(): List<AddressWithCityWithState>
  • 由于列重命名而得到简化,因此 * 而不是用于不明确列名的 AS 子句

使用上面的:-

    allDao = db.getAllDao()

    var state = State()
    state.name = "State1"
    var stateid = allDao.insert(state)
    var city = City()
    city.name = "City1"
    city.statefk = stateid
    var cityid = allDao.insert(city)
    var address = Address()
    address.name = "Address1"
    address.cityfk = cityid
    allDao.insert(address)

    for(awcws: AddressWithCityWithState in allDao.getAllAddressesWithCityAndWithState()) {
        Log.d("DBINFO","${awcws.address.name}, ${awcws.city.name}, ${awcws.state.name}")
    }

日志中的结果是:-

2021-11-22 07:43:28.574 D/DBINFO: Address1, City1, State1

其他工作示例(不更改列名)

不对实体(地址、城市和州)进行任何更改。以下是其他选项的工作示例。

1- 获取完整地址作为单个字符串,所需要的只是查询,例如:-

@Query("SELECT address.name||','||city.name||','||state.name AS fullAddress FROM address JOIN city ON address.cityfk = city.id JOIN state ON city.statefk = state.id ")
fun getAddressesAsStrings(): List<String>
  • 当然,下拉选择器没有多大用处,因为您无法确定这些行来自数据库中的何处。

2 - 具有明确列名的基本 POJO

POJO :-

data class AddressWithCityWithState(
    var address_id: Long,
    var address_name: String,
    var city_id: Long,
    var city_name: String,
    var state_id: Long,
    var state_name: String
)

查询:-

/*
* Returns multiple columns renamed using AS clause to disambiguate
* requires POJO with matching column names
* */
@Query("SELECT " +
        "address.id AS address_id, address.name AS address_name, " +
        "city.id AS city_id, city.name AS city_name, " +
        "state.id AS state_id, state.name AS state_name " +
        "FROM address JOIN city ON address.cityfk = city.id JOIN state ON city.statefk = state.id")
fun getAddressesWithCityAndStateViaBasicPOJO(): List<AddressWithCityWithState>

3- 使用 EMBEDS 的 POJO

POJO :-

data class AddressWithCityWithStateViaEmbeds(
    @Embedded
    var address: Address,
    @Embedded(prefix = cityPrefix)
    var city: City,
    @Embedded(prefix = statePrefix)
    var state: State
)  {
    companion object {
        const val cityPrefix = "city_"
        const val statePrefix = "state_"
    }
}

查询:-

/*
*   Returns multiple columns renamed according to the prefix=? coded in the
*   @Embedded annotation
*
 */
@Query("SELECT address.*, " +
        "city.id AS " + AddressWithCityWithStateViaEmbeds.cityPrefix + "id," +
        "city.name AS " + AddressWithCityWithStateViaEmbeds.cityPrefix + "name," +
        "city.statefk AS " + AddressWithCityWithStateViaEmbeds.cityPrefix + "statefk," +
        "state.id AS " + AddressWithCityWithStateViaEmbeds.statePrefix + "id," +
        "state.name AS " + AddressWithCityWithStateViaEmbeds.statePrefix + "name " +
        "FROM address JOIN city ON address.cityfk = city.id JOIN state ON city.statefk = state.id")
fun getAddressesWithCityAndStateViaEmbedPOJO(): List<AddressWithCityWithStateViaEmbeds>

4- POJO 具有 parent EMBED 和 child RELATE

POJO 的 :-

data class CityWithState(
    @Embedded
    var city: City,
    @Relation(
        entity = State::class,
        parentColumn = "statefk",
        entityColumn = "id"
    )
    var state: State
)

和:-

data class AddressWithCityWithStateViaRelations(
    @Embedded
    var address: Address,
    @Relation(
        entity = City::class, /* NOTE NOT CityWithState which isn't an Entity */
        parentColumn = "cityfk",
        entityColumn = "id"
    )
    var cityWithState: CityWithState
)

和查询:-

@Transaction
@Query("SELECT * FROM address")
fun getAddressesWithCityAndStateViaRelations(): List<AddressWithCityWithStateViaRelations>
  • 注意 @Tranaction 的使用,因此由 Room 构建的底层查询全部在单个数据库事务中完成。

使用上面的

activity 中的以下代码使用所有 4 个输出相同的结果:-

class MainActivity : AppCompatActivity() {

    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val TAG: String = "DBINFO"

        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()

        var state = State(1)
        state.name = "State1"
        val state1Id = dao.insert(state)
        state.id = 2
        state.name = "State2"
        val state2Id = dao.insert(state)

        var city = City(10)
        city.name = "City1"
        city.statefk = state1Id
        val city1Id = dao.insert(city)
        city.id = 11
        city.name = "City2"
        city.statefk = state2Id
        val city2Id = dao.insert(city)
        city.id = 12
        city.name = "City3"
        city.statefk = state1Id
        val city3Id = dao.insert(city)

        var address = Address(100)
        address.name = "Address1"
        address.cityfk = city1Id
        dao.insert(address)
        address.id = address.id + 1
        address.name = "Address2"
        address.cityfk = city2Id
        dao.insert(address)
        address.id = address.id + 1
        address.name = "Address3"
        address.cityfk = city3Id

        for (s: String in dao.getAddressesAsStrings()) {
            Log.d(TAG + "STRG", s)
        }
        for (awcws: AddressWithCityWithState in dao.getAddressesWithCityAndStateViaBasicPOJO()) {
            Log.d(TAG + "BASICPOJO", "${awcws.address_name}, ${awcws.city_name}, ${awcws.state_name}")
        }
        for (awcwsve: AddressWithCityWithStateViaEmbeds in dao.getAddressesWithCityAndStateViaEmbedPOJO()) {
            Log.d(TAG + "EMBEDS","${awcwsve.address.name}, ${awcwsve.city.name}, ${awcwsve.state.name}")
        }
        for(awcwsvr: AddressWithCityWithStateViaRelations in dao.getAddressesWithCityAndStateViaRelations()) {
            Log.d(TAG + "MIXED","${awcwsvr.address.name}, ${awcwsvr.cityWithState.city.name}, ${awcwsvr.cityWithState.state.name}")
        }
    }
}

日志输出为:-

2021-11-22 12:33:54.322 D/DBINFOSTRG: Address1,City1,State1
2021-11-22 12:33:54.322 D/DBINFOSTRG: Address2,City2,State2

2021-11-22 12:33:54.324 D/DBINFOBASICPOJO: Address1, City1, State1
2021-11-22 12:33:54.324 D/DBINFOBASICPOJO: Address2, City2, State2

2021-11-22 12:33:54.326 D/DBINFOEMBEDS: Address1, City1, State1
2021-11-22 12:33:54.326 D/DBINFOEMBEDS: Address2, City2, State2

2021-11-22 12:33:54.332 D/DBINFOMIXED: Address1, City1, State1
2021-11-22 12:33:54.332 D/DBINFOMIXED: Address2, City2, State2