简体   繁体   中英

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

I looked in the android documentation for an answer to my question, but I couldn't find it. To create a recyclerview using the information contained in these classes, how can I get a list of this information in 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 = ""

}

How can I get a list of addresses listing the classes?

How to get a result like this in 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

You would typically have a POJO to represent the combined data. You can then either have a field/variable for the extracted columns noting that values are matched to the liked named variable.

You can use @Embedded to include an entity in it's entirety so in theory embed Address City and State.

  • see variable/column name issues

You can use @Embedded along with @Relation for the child (children) BUT not for grandchildren (eg State). You would need an underlying City with State POJO where City is embedded and State is related by an @Relation.

  • variable/column names are not an issue when using @Relation as room builds underlying queries from the parent.

Variable/Column name issues

Room maps columns to variable according to variable names. So there will be issues with id 's and name columns if using the simpler @Embedded for all three entities.

  • I would suggest always using unique names eg addressId, cityId, StateId, (at least for the column names eg @ColumnInfo(name = "addressId")) but simpler to just have var addressid.

  • An alternative is the use the @Embedded(prefix = "the_prefix") on some, this tells room to match the variable to column name with the prefix so you need to use AS in the SQL. Obviously the_prefix would be changed to suit.

The Dao's

if using @Embedded with @Relation then you simply need to get the parent so

@Query("SELECT * FROM address")
fun getAddressWithCityAndWithState(): List<AddressWithCityAndWithState>
  • where AddressWithCityAndWithState is the POJO that has the Address @Embedded and the CityWithState with @Relation.

You would also need the accompanying CityWithState POJO with City @Embedded and State with @Relation.

If Embedding Address, City and State with City having a prefix of "city_" and state having a prefix of "state_" then you would use something like:-

@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>
  • where AddressWithCityAndWithState is the POJO that has Address, City and State @Embedded

Note the above is in-principle.

Working Example

The following is a working example based upon

  • a) renaming the columns to avoid ambiguity and
  • b) using @Embedded of all three classes in the POJO AddressWithCityWithState

First changes to the Address, City and State to rename the columns:-

Address :-

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

City :-

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

State :-

@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 = ""
}

Next the POJO AddressWithCityWithState :-

data class AddressWithCityWithState (
    @Embedded
    val address: Address,
    @Embedded
    val city: City,
    @Embedded
    val state: State
)
  • due to unique column names no prefix =? required

A suitable 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>
  • simplified due to column renaming so * instead AS clauses for ambiguous column names

Using the above:-

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

The result in the log being:-

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM