I have an API that returns a DTO containing entities with the one-to-many relationships filled in a list.
I'm struggling to figure out how work with that data in Room.
I need to map this DTO to the corresponding Entities. I then need to insert them in the database. And later on, I want to query them and retrieve a BoardEntity with a list of its corresponding BoardChildEntities
fun getBoards() = networkBoundResource(query = {
// I need to query all boards from Room and add to that its corresponding children in a list
},
fetch = {
api.getBoards()
},
saveFetchResult = { dtos ->
// how can I save all the data in the DTOs in their corresponding tables
// without writing a lot of nested loops
})
The DTO returned from the api:
data class BoardDto(
val id: Int,
val name: String,
val boardChildren: List<BoardChildDto>,
) {
data class BoardChildDto(
val id: Int,
val boardId: Int, // foreign key
val name: String,
val boardElements: List<BoardElementDto>,
) {
data class BoardElementDto(
val id: Int,
val boardChildId: Int, // foreign key
val name: String,
val type: String,
val hyperlinks: List<BoardElementHyperlinkDto>,
) {
data class BoardElementHyperlinkDto(
val id: Int,
val boardElementId: Int, // foreign key
val name: String,
)
}
}
}
The Room entities:
#Entity
data class BoardEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val icon: String,
val name: String,
)
#Entity(
foreignKeys = [ForeignKey(
entity = BoardEntity::class,
parentColumns = ["id"],
childColumns = ["boardId"],
onDelete = ForeignKey.CASCADE
)]
)
data class BoardChildEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val boardId: Int,
val name: String,
)
#Entity(
foreignKeys = [ForeignKey(
entity = BoardChildEntity::class,
parentColumns = ["id"],
childColumns = ["boardChildId"],
onDelete = ForeignKey.CASCADE
)]
)
data class BoardElementEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val boardChildId: Int,
val name: String,
)
#Entity(
foreignKeys = [ForeignKey(
entity = BoardElementEntity::class,
parentColumns = ["id"],
childColumns = ["boardElementId"],
onDelete = ForeignKey.CASCADE
)]
)
data class BoardElementHyperlinkEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val boardElementId: Int,
val name: String,
)
The mappers from DTOs to Room entities
fun BoardDto.toEntityModel(): BoardEntity {
return BoardEntity(
id = id,
name = name,
)
}
fun BoardChildDto.toEntityModel(): BoardChildEntity {
return BoardChildEntity(
id = id,
boardId = boardId,
name = name,
)
}
fun BoardElementDto.toEntityModel(): BoardElementEntity {
return BoardElementEntity(
id = id,
boardChildId = boardChildId,
name = name,
)
}
fun BoardElementHyperlinkDto.toEntityModel(): BoardElementHyperlinkEntity {
return BoardElementHyperlinkEntity(
id = id,
boardElementId = boardElementId,
name = name,
)
}
// how can I save all the data in the DTOs in their corresponding tables
// without writing a lot of nested loops
Depends upon what you call lots, some cannot really be avoided.
Here's is an example of how this can be achieved (from an #Dao annotated interface):-
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardEntity: BoardEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardChildEntity: BoardChildEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardElementEntity: BoardElementEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardElementHyperlinkEntity: BoardElementHyperlinkEntity): Long
#Transaction
#Query("")
fun insertDTO(dto: BoardDto, icon: String): Int {
val boardId = insert(BoardEntity( id = dto.id,name = dto.name, icon = icon))
if (boardId < 0) return TheDatabase.BOARD_NOT_INSERTED
for (bc in dto.boardChildren) {
val boardChildId = insert(BoardChildEntity(id = bc.id, boardId = boardId.toInt(), name = bc.name))
if (boardChildId < 0) return TheDatabase.BOARDCHILD_NOT_INSERTED
for (be in bc.boardElements) {
val boardElementId = insert(BoardElementEntity(id = be.id, boardChildId = boardChildId.toInt(), name = be.name))
if (boardElementId < 0) return TheDatabase.BOARDELEMENT_NOT_INSERTED
for (beh in be.hyperlinks) {
val boardElementHyperlinkId = insert(BoardElementHyperlinkEntity(id = beh.id, boardElementId = boardElementId.toInt(), beh.name))
if (boardElementHyperlinkId < 0) return TheDatabase.BOARDHYPERLINK_NOT_INESRTED
}
}
}
return boardId.toInt()
}
So initially the 4 standard convenience inserts
Then a function with a body that 3 nested loops (noting that for the sake of less hassle demonstrating that the foreign keys are cascaded and overwrite the hard code values (see demo and results)). Of the actual values if correct could be used (if not then you would encounter foreign key conflicts). In a similar vein if an insert is IGNORED at whatever level then an immediate return is made, an Int is returned according to come constants (-99 through to -96 inclusive) (this behaviour can easily be removed).
And later on, I want to query them and retrieve a BoardEntity with a list of its corresponding BoardChildEntities
and I assume the with the child BoardElements and also with the HyperLink children (down through all the hierarchy).
For this you use a hierarchy of POJO's each getting the parent and the children. e.g. the following POJO's with the parent having the #Embed annotation and the list of children having the #Relation annotation.
data class BoardElementEntityWithHyperlinkEntity(
#Embedded
val boardElementEntity: BoardElementEntity,
#Relation( entity = BoardElementHyperlinkEntity::class, parentColumn = "id", entityColumn = "boardElementId")
val boardElementHyperlinkEntityList: List<BoardElementHyperlinkEntity>
)
data class BoardChildEntityWithElementEntity(
#Embedded
val boardChildEntity: BoardChildEntity,
#Relation( entity = BoardElementEntity::class, parentColumn = "id", entityColumn = "boardChildId")
val boardElementEntityList: List<BoardElementEntityWithHyperlinkEntity>
)
data class BoardEntityWithBoardChildList(
#Embedded
val boardEntity: BoardEntity,
#Relation(entity = BoardChildEntity::class, parentColumn = "id", entityColumn = "boardId")
val BoardChildEntityList: List<BoardChildEntityWithElementEntity>
)
Note in the #Relation how the entity= is the #Entity annotated class not the class of the field.
The POJO's are listed in reverse order as experience has shown that it is easier to create them from the bottom up.
DEMO
Here's a working demo that when run takes adds the data from a BoardDTO and then extracts it (albeit that some references, such as boardId, are replaced).
First the entire code for the Database Stuff and also your BoardDTo (your mappers haven't been used):-
data class BoardDto(
val id: Int,
val name: String,
val boardChildren: List<BoardChildDto>,
) {
data class BoardChildDto(
val id: Int,
val boardId: Int, // foreign key
val name: String,
val boardElements: List<BoardElementDto>,
) {
data class BoardElementDto(
val id: Int,
val boardChildId: Int, // foreign key
val name: String,
val type: String,
val hyperlinks: List<BoardElementHyperlinkDto>,
) {
data class BoardElementHyperlinkDto(
val id: Int,
val boardElementId: Int, // foreign key
val name: String,
)
}
}
}
#Entity
data class BoardEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val icon: String,
val name: String,
)
#Entity(
foreignKeys = [ForeignKey(
entity = BoardEntity::class,
parentColumns = ["id"],
childColumns = ["boardId"],
onDelete = ForeignKey.CASCADE
)]
)
data class BoardChildEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val boardId: Int,
val name: String,
)
data class BoardEntityWithBoardChildList(
#Embedded
val boardEntity: BoardEntity,
#Relation(entity = BoardChildEntity::class, parentColumn = "id", entityColumn = "boardId")
val BoardChildEntityList: List<BoardChildEntityWithElementEntity>
)
#Entity(
foreignKeys = [ForeignKey(
entity = BoardChildEntity::class,
parentColumns = ["id"],
childColumns = ["boardChildId"],
onDelete = ForeignKey.CASCADE
)]
)
data class BoardElementEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val boardChildId: Int,
val name: String,
)
data class BoardChildEntityWithElementEntity(
#Embedded
val boardChildEntity: BoardChildEntity,
#Relation( entity = BoardElementEntity::class, parentColumn = "id", entityColumn = "boardChildId")
val boardElementEntityList: List<BoardElementEntityWithHyperlinkEntity>
)
#Entity(
foreignKeys = [ForeignKey(
entity = BoardElementEntity::class,
parentColumns = ["id"],
childColumns = ["boardElementId"],
onDelete = ForeignKey.CASCADE
)]
)
data class BoardElementHyperlinkEntity(
#PrimaryKey(autoGenerate = false) val id: Int,
val boardElementId: Int,
val name: String,
)
data class BoardElementEntityWithHyperlinkEntity(
#Embedded
val boardElementEntity: BoardElementEntity,
#Relation( entity = BoardElementHyperlinkEntity::class, parentColumn = "id", entityColumn = "boardElementId")
val boardElementHyperlinkEntityList: List<BoardElementHyperlinkEntity>
)
#Dao
interface TheDAOs {
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardEntity: BoardEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardChildEntity: BoardChildEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardElementEntity: BoardElementEntity): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardElementHyperlinkEntity: BoardElementHyperlinkEntity): Long
#Transaction
#Query("")
fun insertDTO(dto: BoardDto, icon: String): Int {
val boardId = insert(BoardEntity( id = dto.id,name = dto.name, icon = icon))
if (boardId < 0) return TheDatabase.BOARD_NOT_INSERTED
for (bc in dto.boardChildren) {
val boardChildId = insert(BoardChildEntity(id = bc.id, boardId = boardId.toInt(), name = bc.name))
if (boardChildId < 0) return TheDatabase.BOARDCHILD_NOT_INSERTED
for (be in bc.boardElements) {
val boardElementId = insert(BoardElementEntity(id = be.id, boardChildId = boardChildId.toInt(), name = be.name))
if (boardElementId < 0) return TheDatabase.BOARDELEMENT_NOT_INSERTED
for (beh in be.hyperlinks) {
val boardElementHyperlinkId = insert(BoardElementHyperlinkEntity(id = beh.id, boardElementId = boardElementId.toInt(), beh.name))
if (boardElementHyperlinkId < 0) return TheDatabase.BOARDHYPERLINK_NOT_INESRTED
}
}
}
return boardId.toInt()
}
#Transaction
#Query("SELECT * FROM boardentity")
fun getAllBoardsWithFamily(): List<BoardEntityWithBoardChildList>
}
#Database(
entities = [
BoardElementHyperlinkEntity::class,
BoardElementEntity::class,
BoardChildEntity::class,
BoardEntity::class
],
exportSchema = false,
version = 1
)
abstract class TheDatabase: RoomDatabase() {
abstract fun getTheDAOs(): TheDAOs
companion object {
const val BOARD_NOT_INSERTED = -99
const val BOARDCHILD_NOT_INSERTED = -98
const val BOARDELEMENT_NOT_INSERTED = -97
const val BOARDHYPERLINK_NOT_INESRTED = -96
private var instance: TheDatabase?=null
fun getInstance(context: Context): TheDatabase {
if (instance==null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
To actually test then the following activtiy code (MainActivity):-
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()
val beh = BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(11100,9999,"BEH0001")
val behlist1 = listOf(
beh,
BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(22200,9999,"BEH0002"),
BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(33300,9999,"BEH0003")
)
val behlist2 = listOf(
BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(44400,9999,"BEH0004"),
BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(55500,9999,"BEH0005")
)
val belist1 = listOf(
BoardDto.BoardChildDto.BoardElementDto(id = 1100, boardChildId = 999, name = "BE0001", type = "A", hyperlinks = behlist1),
BoardDto.BoardChildDto.BoardElementDto(id= 2200, boardChildId = 999, name = "BE0002", type = "B", hyperlinks = behlist2)
)
val bclist = listOf(
BoardDto.BoardChildDto(id = 110, boardId = 99, name = "BC0001", boardElements = belist1 ),
BoardDto.BoardChildDto(id = 220, boardId = 99, name = "BC0002", boardElements = belist1 ),
)
dao.insertDTO(BoardDto(id = 11, boardChildren = bclist, name = "B00001"), icon = "unsure")
for (b in dao.getAllBoardsWithFamily()) {
Log.d(TAG,"Board Name is ${b.boardEntity.name} ICON is ${b.boardEntity.icon} ID is ${b.boardEntity.id}. Board has ${b.BoardChildEntityList.size} BoardChildren. They are:-")
for (bc in b.BoardChildEntityList) {
Log.d(TAG,"\tBC Name is ${bc.boardChildEntity.name} ID is ${bc.boardChildEntity.id} Mapped To Board ${bc.boardChildEntity.boardId}. ${bc.boardElementEntityList.size} elements:-")
for(be in bc.boardElementEntityList) {
Log.d(TAG,"\t\tBE Name is ${be.boardElementEntity.name} ${be.boardElementHyperlinkEntityList.size} elemets:-")
for (beh in be.boardElementHyperlinkEntityList) {
Log.wtf(TAG,"\t\t\tBEH name is ${beh.name} ID is ${beh.id} maps to ${beh.boardElementId}")
}
}
}
}
}
companion object {
const val TAG = "DBINFO"
}
}
Note the code is only intended to run the once
When run then the result output to the log is:-
D/DBINFO: Board Name is B00001 ICON is unsure ID is 11. Board has 2 BoardChildren. They are:-
D/DBINFO: BC Name is BC0001 ID is 110 Mapped To Board 11. 2 elements:-
D/DBINFO: BE Name is BE0001 3 elemets:-
D/DBINFO: BEH name is BEH0001 ID is 11100 maps to 1100
D/DBINFO: BEH name is BEH0002 ID is 22200 maps to 1100
D/DBINFO: BEH name is BEH0003 ID is 33300 maps to 1100
D/DBINFO: BE Name is BE0002 2 elemets:-
D/DBINFO: BEH name is BEH0004 ID is 44400 maps to 2200
D/DBINFO: BEH name is BEH0005 ID is 55500 maps to 2200
D/DBINFO: BC Name is BC0002 ID is 220 Mapped To Board 11. 0 elements:-
note BC0002 will have no children as the list was the same as for BC0001 and thus the duplicates would have been skipped.
The database via App inspection:-
I have the following 3 entities:
#Entity(tableName = "PROPERTY",
indices = [
androidx.room.Index(value = ["id"], unique = true)
])
data class Property (
#Expose #PrimaryKey override val id: UUID = UUID.randomUUID(),
#SerializedName("type") #ColumnInfo(name = "type") val type: ItemPropertyType,
#SerializedName("code") #ColumnInfo(name = "code") val code: String,
#SerializedName("default_description") #ColumnInfo(name = "default_description") val defaultDescription: String?,
#SerializedName("description_id") #ColumnInfo(name = "description_id") val descriptionId: UUID?,
#SerializedName("default_property") #ColumnInfo(name = "default_property") val defaultProperty: Boolean,
#SerializedName("entity") #ColumnInfo(name = "entity") val entity: String = "ITEM"
)
#Entity(tableName = "PROPERTY_SET_DETAIL",
indices = [
Index(value = ["id"], unique = true),
Index(value = ["property_id"], unique = false)
],
foreignKeys = [
ForeignKey(entity = PropertySet::class, parentColumns = ["id"], childColumns = ["property_set_id"]),
ForeignKey(entity = Property::class, parentColumns = ["id"], childColumns = ["property_id"])])
data class PropertySetDetail (
#Expose #PrimaryKey override val id: UUID = UUID.randomUUID(),
#SerializedName("property_id") #ColumnInfo(name = "property_id") val propertyId: UUID,
#SerializedName("value") #ColumnInfo(name = "value") var value: String?,
#SerializedName("rank") #ColumnInfo(name = "rank") val rank: Int,
#SerializedName("property_set_id") #ColumnInfo(name = "property_set_id") val propertySetId: UUID
)
#Entity(tableName = "PROPERTY_SET",
indices = [Index(value = ["id"], unique = true), Index(value = ["properties_charset"], unique = false), Index(value = ["hashkey"], unique = false)])
data class PropertySet (
#Expose #PrimaryKey override val id: UUID = UUID.randomUUID(),
#SerializedName("item_id") #ColumnInfo(name = "item_id") val itemId: UUID,
#SerializedName("properties_charset") #ColumnInfo(name = "properties_charset") var propertiesCharset: String?,
#SerializedName("hashkey") #ColumnInfo(name = "hashkey") var hashkey: String?,
)
and also the following data classes that I need for the nested relationships among the 3 entities declared above:
data class PropertySetAndDetails(
#Embedded
val set: PropertySet,
#Relation(
entity = PropertySetDetail::class,
parentColumn = "id",
entityColumn = "property_set_id"
) val details: List<PropertyDetailAndProperty>,
)
data class PropertyDetailAndProperty(
#Embedded
val property: Property,
#Relation(
parentColumn = "id",
entityColumn = "property_id"
) val detail: PropertySetDetail
)
and the following DAO query function:
#Transaction
#Query("select * from property_set where item_id = :itemId")
suspend fun getSetsForItem(itemId: UUID): List<PropertySetAndDetails>
There is no way to make it work. I get the following build error:
error: There is a problem with the query: [SQLITE_ERROR] SQL error or
missing database (no such column: type)
I also declared the following convert functions for
ItemPropertyType:
#TypeConverter
fun toItemPropertyType(v: Byte?): ItemPropertyType {
return when (v) {
null -> ItemPropertyType.Unknown
else -> ItemPropertyType.getByValue(v)
}
}
#TypeConverter
fun fromItemPropertyType(t: ItemPropertyType?): Byte {
return when (t) {
null -> ItemPropertyType.Unknown.value
else -> t.value
}
}
Can someone explain me why this nested relationship doesn't work ? Thanks in advance.
Can someone explain me why this nested relationship doesn't work ?
I can't (let's call it Room's magic), but I can hint you how to make this work. Try to change your PropertyDetailAndProperty class to this:
data class PropertyDetailAndProperty(
#Embedded
val detail: PropertySetDetail,
#Relation(
parentColumn = "property_id",
entityColumn = "id"
) val property: Property
)
It seems nested class should contain entity you use in outer class ( PropertySetDetail in your case) beyond #Relation. Why? Well, I guess it just was developers' design.
Have you tried using the converter? Room cannot identify this data ("ItemPropertyType"). This is why you are getting this error.
Doc
I'm creating a small project with Room and three tables (jobs, tags, categories).
Essentially:
A job belongs to (or has) 1 and only category, but a job can have 0..N tags.
A category has 0..N jobs.
A tag has 0..N jobs.
I'm having some trouble trying to model all the data, especially when it comes to the relationships between the entities. More concretely, I have:
fun JobListing.toJobEntityList(): List<JobEntity> {
val jobEntityList = mutableListOf<JobEntity>()
this.jobs.take(50).forEach { job: Job ->
jobEntityList.add(
JobEntity(
jobId = job.id,
title = job.title,
url = job.url,
companyName = job.companyName,
jobType = job.jobType,
publicationDate = job.publicationDate,
relocation = job.candidateRequiredLocation,
salary = job.salary
)
)
}
return jobEntityList
}
This extension function is being called when I'm fetching the data from the network, so I can convert it to entities and store them in my DB.
I'm essentially creating a JobEntity, but a job should have 1 category and 0..N tags associated. The problem is that I don't know how to add that data related to the relationship between a job and its category and tags.
This is my JobEntity class:
#Entity(
tableName = "Jobs",
indices = [
Index("id", unique = true)
]
)
data class JobEntity(
#PrimaryKey #ColumnInfo(name = "id") val jobId: Int,
#ColumnInfo(name = "title") val title: String,
#ColumnInfo(name = "url") val url: String,
#ColumnInfo(name = "company_name") val companyName: String,
#ColumnInfo(name = "job_type") val jobType: String,
#ColumnInfo(name = "publication_date") val publicationDate: String,
#ColumnInfo(name = "candidate_required_location") val relocation: String,
#ColumnInfo(name = "salary") val salary: String
)
Thanks in advance!
Hi you should model your database like this:
Job_Category
Tags
Jobs (add category_id field and add 1 foreign key referencing to Category table)
Job_Tags (add tag_id, job_id fields and add 2 foreign keys referencing to Category and Jobs tables)
#Entity(tableName = "Category")
data class CategoryEntity(
#PrimaryKey #ColumnInfo(name = "id") val id: Int,
)
#Entity(tableName = "Tags")
data class TagEntity(
#PrimaryKey #ColumnInfo(name = "id") val id: Int,
)
#Entity(
tableName = "Jobs",
foreignKeys = [ForeignKey(entity = CategoryEntity::class, parentColumns = ["id"], childColumns = ["categoryid"])]
)
data class JobEntity(
#PrimaryKey #ColumnInfo(name = "id") val id: Int,
#ColumnInfo(name = "categoryid", index = true) val categoryid: Int,
// ... other fields
)
#Entity(
tableName = "JobTags",
foreignKeys = [
ForeignKey(entity = TagEntity::class, parentColumns = ["id"], childColumns = ["tagId"]),
ForeignKey(entity = JobEntity::class, parentColumns = ["id"], childColumns = ["jobId"]),
]
)
data class JobTagEntity(
#PrimaryKey #ColumnInfo(name = "id") val id: Int,
#ColumnInfo(name = "tagId", index = true) val tagId: Int,
#ColumnInfo(name = "jobId", index = true) val jobId: Int,
)
I have three tables and I have a many to many relationship here:
Contact table:
#Entity(tableName = "T_Contacts")
class Contact(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "_id") var id: Long,
#ColumnInfo(name = "phoneNumber")
var phoneNumber: String = "",
#ColumnInfo(name = "name")
var name: String = "",
#ColumnInfo(name = "active")
var active: Int = 1
#ColumnInfo(name = "contactId")
var contactId: Long = -1
#ColumnInfo(name = "phoneContactId")
var phoneContactId: String = ""
}
Email table:
#Entity(tableName = "T_EmailAddress")
class EmailAddress(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "_id")
var id : Long,
#ColumnInfo(name="emailAddress")
var emailAddress: String,
#ColumnInfo(name="active")
var active : Int) {
}
And the associative table:
#Entity(tableName = "T_EmailAddressContact",
foreignKeys = [ForeignKey(
entity = Contact::class,
parentColumns = arrayOf("contactId"),
childColumns = arrayOf("contactId"),
onDelete=ForeignKey.CASCADE),
ForeignKey(
entity = EmailAddress::class,
parentColumns = arrayOf("emailId"),
childColumns = arrayOf("emailId"),
onDelete=ForeignKey.CASCADE)]
)
class EmailAddressContactCrossRef(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "_id")
var id: Long,
#ColumnInfo(name="contactId")
var contactId: Long,
#ColumnInfo(name="emailId")
var emailId : Long,
#ColumnInfo(name="active")
var active : Int ) {
How can I get a list of all the email contacts using relations? I tried:
data class EmailAddressContact(
#Embedded val contact: EmailAddress,
#Relation(parentColumn = "contactId",
entityColumn = "contactId",
associateBy = Junction(EmailAddressContactCrossRef::class)
)
val listContacts: List<Contact>
)
but this only gets me a list of all contacts for a certain email address. I want something like this:
data class EmailAddressContact {
val id: Long,
val emailAddress: EmailAddress,
val contact: Contact,
val active: Boolean
}
by calling a query:
#Transaction
#Query("SELECT * FROM T_EmailAddressContact")
fun getAllEmailAddressAndContacts(): List<EmailAddressContact>
According to your EmailAddressContact structure it seems you don't need to use junction, just relations. Try this way:
data class EmailAddressContact(
#Embedded val emailAddressContactCrossRef: EmailAddressContactCrossRef, // <- there you'll get "id" and "active" fields
#Relation(
parentColumn = "contactId",
entityColumn = "contactId"
)
val contact: Contact,
#Relation(
parentColumn = "emailId",
entityColumn = "_id"
)
val emailAddress: EmailAddress
)
and query you mentioned in your question should fit
Given
#Entity(
tableName = "state",
foreignKeys = arrayOf(
ForeignKey(entity = Foo::class, parentColumns = arrayOf("id"), childColumns = arrayOf("foo_id")),
ForeignKey(entity = Bar::class, parentColumns = arrayOf("id"), childColumns = arrayOf("bar_id"))
)
)
data class State(
#PrimaryKey(autoGenerate = true) val id: Long = 1
) {
#ColumnInfo(name = "foo_id")
var fooId: Long? = null
#ColumnInfo(name = "bar_id")
var barId: Long? = null
}
#Entity(tableName = "foo")
open class Foo(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
open val id: Long,
#ColumnInfo(name = "foo")
val foo: String?,
)
#Entity(tableName = "bar")
open class Bar(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
open val id: Long,
#ColumnInfo(name = "bar")
val bar: String?,
)
I am trying to create a Join POJO to store the results of the query:
class FooBar(
#Embedded
val foo: Foo,
#Embedded
val bar: Bar
)
And my failed attempt at a query:
#Query("SELECT foo.*, bar.* FROM foo, bar JOIN state ON foo.id == state.foo_id JOIN bar ON bar.id == session.bar_id ")
fun getFooBar(): LiveData<FooBar>
However I get an error when compiling. Do I need to deconflict the id fields in foo and bar since they are named the same?
I have tried with a prefix, no luck:
class FooBar(
#Embedded(prefix = "foo_")
val foo: Foo,
#Embedded(prefix = "bar_")
val bar: Bar
)
Any ideas?
Here is an example that might help you.
#Query("SELECT RoomArticle.*, RoomBranch.id AS 'RoomBranch_id', RoomBranch.name AS 'RoomBranch_name' "
Data entity:
public class RoomArticleOfBranch {
#Embedded
public RoomArticle article;
#Embedded(prefix = "RoomBranch_")
public RoomBranch branch;
Notice I provide prefix both in query and in #Embedded.
As you're trying Embedded try to change the query as follows
#Dao
interface TestDao {
#Query("SELECT foo.id as foo_id,bar.id as bar_id,bar.bar as bar_bar FROM foo, bar JOIN state ON foo_id = state.foo_id and bar_id = state.bar_id")
fun getFooBar(): LiveData<FooBar>
}
There are two things need to be fixed.
You need to rename the column name during the select query.
fixing your joining query.