Listing All data Based on foreign keys android room - android

I've googled my question and i can't find the answer yet.
I want to get all the meals that is in favorite, below is the code and what i have tried.
The result is that i only get a single Meal, even though there are 3 data in dbfavoritemeal.
The expected result is that i will get all the meals based on all the mealid in dbfavoritemeal.
Please guide me
I have a meal class
#Entity
data class DbMeal(
#PrimaryKey val id: Long,
val name: String,
val thumbnailUrl: String,
val category: String,
val instructions: String = "",
) {
And then i have favorite class
#Entity(
foreignKeys = [
ForeignKey(
entity = DbMeal::class,
parentColumns = ["id"],
childColumns = ["mealId"],
onDelete = ForeignKey.CASCADE
)],
indices = [Index(
value = ["mealId"],
unique = true
)]
)
data class DbFavoriteMeal(
#PrimaryKey
val mealId: Long
)
What i've tried is in DAO
#Query("select * from dbMeal where id = (select mealId from dbfavoritemeal)")
suspend fun getAllFavoriteDbMeal(): List<DbMeal>

You can change your DAO like
#Query("select * from dbMeal where id in (select mealId from dbfavoritemeal)")
suspend fun getAllFavoriteDbMeal(): List<DbMeal>
or you can add isFavorite parameter to your Entity.
#Entity
data class DbMeal(
#PrimaryKey val id: Long,
val name: String,
val thumbnailUrl: String,
val category: String,
val instructions: String = "",
val isFavorite: Boolean = false,
)
And your DAO should look like
#Query("select * from dbMeal where isFavorite = 1")
suspend fun getAllFavoriteDbMeal(): List<DbMeal>

Related

How to insert and query nested entities in Room database received from API that contains one to many relationships

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

How to query from Multiple Tables

I have three Models
#Entity(foreignKeys = [ForeignKey(entity = SelfHelpGroup::class, parentColumns = ["shgId"], childColumns = ["shgId"], onDelete = CASCADE), ForeignKey(entity = Member::class, parentColumns = ["memberId"], childColumns = ["memberId"], onDelete = CASCADE)])
data class Committee(
#PrimaryKey(autoGenerate = true)
#SerializedName("committeeId")
val committeeId: Int?= null,
#SerializedName("shgId")
val shgId: Int?,
#SerializedName("memberId")
val memberId: Int?,
#SerializedName("date")
val date: String?
)
#Entity(tableName = "Member", foreignKeys = [ForeignKey(entity = SelfHelpGroup::class, parentColumns = ["shgId"], childColumns = ["shgId"], onDelete = CASCADE), ForeignKey(entity = Role::class, parentColumns = ["roleId"], childColumns = ["roleId"], onDelete = CASCADE)])
data class Member(
#PrimaryKey(autoGenerate = true)
#SerializedName("memberId")
val memberId: Int ?= null,
#SerializedName("shgId")
val shgId: Int,
#SerializedName("name")
val name: String,
#SerializedName("address")
val address: String,
#SerializedName("phoneNumber")
val phoneNumber: String,
#SerializedName("emailId")
val emailId: String,
#SerializedName("roleId")
val roleId: Int?,
#SerializedName("password")
val password: String?
)
#Entity(foreignKeys = [
ForeignKey(entity = Committee::class, parentColumns = ["committeeId"], childColumns = ["committeeId"], onDelete = CASCADE),
ForeignKey(entity = Member::class, parentColumns = ["memberId"], childColumns = ["memberId"], onDelete = CASCADE),
])
data class Attendance(
#PrimaryKey(autoGenerate = true)
#SerializedName("attendanceId")
val attendanceId: Int?= null,
#SerializedName("committeeId")
val committeeId: Int,
#SerializedName("memberId")
val memberId: Int,
/*#SerializedName("status")
val status: AttendanceStatus,*/
#SerializedName("isPresent")
var isPresent: Boolean = false,
#SerializedName("leaveApplied")
var leaveApplied: Boolean = false
)
Relation between 3 models :
Any member can host a committee.
The hosted memberId is saved in the table Member.
Other members can join the committee.
To track the attendance of these members, we are using the Table Attendance.
So I need help queriying the data in such a way that the result structure would look like below
data class CommitteeDetails (
val committeeId: Int,
val member: Member,
val attendances: List<Attendance>,
val dateTime: String
)
Since there are more than many committees, I need to query to get Listof CommitteeDetails
val committees = List<CommitteeDetails>()
The easiest way would be to use:-
data class CommitteeDetails (
//val committeeId: Int, /* part of the Committee so get the Committee in it's entireity */
#Embedded
val committee: Committee,
#Relation(entity = Member::class, parentColumn = "memberId", entityColumn = "memberId")
val member: Member,
#Relation(entity = Attendance::class, parentColumn = "committeeId", entityColumn = "committeeId")
val attendances: List<Attendance>
//val dateTime: String
)
This does then retrieve a little more information but the Query is very simple as you just query the committee table.
e.g. to get ALL Committees with the Member and the attendances then you can use
#Transaction
#Query("SELECT * FROM committee")
#Tranaction is not mandatory but if not used will result in a warning e.g.
warning: The return value includes a POJO with a `#Relation`. It is usually desired to annotate this method with `#Transaction` to avoid possibility of inconsistent results between the POJO and its relations. See https://developer.android.com/reference/androidx/room/Transaction.html for details.
This is because #Relation results in Room effectively running subqueries to obtain the related items. Note that using #Relation will return ALL the related items for the #Embedded.
IF you wanted to get exactly what you have asked for then it's a little more convoluted as you would then have to not use #Embedded for the Committee and thus you could then not use #Relation.
In theory you would SELECT FROM the committee table, JOIN the member table and also JOIN the attendance table. The issue is that the result is the cartesian map so for every attendance per committee you would get a result that contained the committee columns (id and date) the member columns (all to build the full Member class) and all the columns from the attendance. However, the issue, is then building the CommitteeDetails.
However, you can mimic how room works and just get the desired committee column along with the member columns and then invoke a subquery to obtain the related attendances (potentially filtering them).
So say you have (wanting a List of these as the end result):-
data class CommitteeDetailsExact (
val committeeId: Int,
val member: Member,
val attendances: List<Attendance>,
val dateTime: String
)
The to facilitate the initial extraction of the committee and members columns you could have another POJO such as:-
data class CommitteeIdAndDateAsDateTimeWithMember(
val committeeId: Int,
#Embedded
val member: Member,
val dateTime: String
)
To facilitate extracting the data you could have functions such as:-
#Query("SELECT committee.committeeId, committee.date AS dateTime, member.* FROM committee JOIN member ON committee.memberId = member.memberId")
fun getCommitteeExactLessAttendances(): List<CommitteeIdAndDateAsDateTimeWithMember>
#Query("SELECT * FROM attendance WHERE committeeId=:committeeId")
fun getCommitteeAttendances(committeeId: Int): List<Attendance>
To obtain the end result then the above functions need to be used together, so you could have a function such as:-
#Transaction
#Query("")
fun getExactCommitteeDetails(): List<CommitteeDetailsExact> {
var rv = arrayListOf<CommitteeDetailsExact>()
for (ciadadwm in getExactCommitteeDetails()) {
rv.add(CommitteeDetailsExact(ciadadwm.committeeId,ciadadwm.member,getCommitteeAttendances(ciadadwm.committeeId),ciadadwm.dateTime))
}
return rv.toList()
}
This will:-
return the desired list of committee details (albeit List<CommitteeDetailsExact> to suite the two answers given)
run as a single transaction (the #Query(") enables Room to apply the #Transaction)
Obtains the list of committee and member columns an then
Loops through the list extract the respective list of attendances
in short it, in this case, is very much similar to the first answer other than limiting the columns extracted from the committee table.

Android Room cascade delete for a one to many relationship

Say I have two entities, Workout and Exercise and a one to many relationship exists between Workout (one) and Exercise (many). The entities are setup like this
Workout Entity:
#Entity(
tableName = "workouts",
indices = [Index("startDate")]
)
data class Workout(
#PrimaryKey
val startDate: String,
val workoutName: String
)
Exercise Entity:
#Entity
data class Exercise(
#PrimaryKey(autoGenerate = true)
val exerciseId: Long = 0,
val workoutId: String,
val name: String
)
Workout with Exercises:
#Entity(
foreignKeys = [ForeignKey(
entity = Workout::class,
parentColumns = arrayOf("startDate"),
childColumns = arrayOf("workoutId"),
onDelete = ForeignKey.CASCADE
)]
)
data class Exercise(
#PrimaryKey(autoGenerate = true)
val exerciseId: Long = 0,
val workoutId: String,
val name: String
)
This is how I get the exercises related to a workout:
#Transaction
#Query("SELECT * FROM workouts WHERE startDate = :startDate")
suspend fun getWorkoutWithExercises(startDate: String): WorkoutWithExercises
So my question is, if the workout instance containing exercises is deleted, will the related exercises also be deleted? If not, how would this be accomplished?
Thanks
The exercises will also be deleted as you have created a Foreign Key for the table Exercise.

Join 2 foreign key relationships into one object in query

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.

Querying table A with inner join on table B does not return columns from table B in an Android application using Room database

I'm trying to query from posts table with inner join on users and its respective primary and foreign key:
#Query("SELECT p.*, u.id AS userId, u.name AS userName, u.username AS userUsername FROM posts p JOIN users u ON p.userId = u.id")
fun fetchAllPostsAndUsers(): Observable<List<Post>>
These are the POJOs. First, User:
#Entity(tableName = "users")
data class User(
#PrimaryKey(autoGenerate = true)
#SerializedName("id")
#ColumnInfo(name = "id")
val uid: Int,
val name: String,
val username: String,
val email: String,
#Embedded
val address: Address?,
val phone: String,
val website: String,
#Embedded
val company: Company?
)
and Post:
#Entity(
tableName = "posts",
foreignKeys = [ForeignKey(
entity = User::class,
parentColumns = ["id"],
childColumns = ["userId"],
onDelete = ForeignKey.CASCADE
)]
)
data class Post(
#PrimaryKey(autoGenerate = true)
#SerializedName("id")
#ColumnInfo(name = "id")
val pid: Int,
val userId: Int,
val title: String,
val body: String
)
But the result of the query above is only the columns from the posts table, without any column from the users', despite being included in the query when fetching from the database.
I guess you should take a look to this thread.
Post entity do not contain any User object. You should create a model that hold both.
The solution for this was to add fields matching the name of the columns or alias from the SQL query, and not these #Embeddeds.

Categories

Resources