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.
Related
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.
I have a many to many relationship Room database with three tables:
First one :
data class Name(
#PrimaryKey(autoGenerate = true)
var nameId : Long = 0L,
#ColumnInfo(name = "name")
var name : String = "",
#ColumnInfo(name = "notes")
var notes: String=""
)
Second:
#Entity(tableName = "tags_table")
data class Tag(
#PrimaryKey(autoGenerate = true)
var tagId : Long = 0L,
#ColumnInfo(name = "tag_name")
var tagName : String = ""
)
Third:
#Entity(
tableName = "tagInName_table",
primaryKeys = ["nameId", "tagId"],
foreignKeys = [
ForeignKey(
entity = Name::class,
parentColumns = ["nameId"],
childColumns = ["nameId"]
),
ForeignKey(
entity = Tag::class,
parentColumns = ["tagId"],
childColumns = ["tagId"]
)
]
)
data class TagInName(
#ColumnInfo(name = "nameId")
var nameId: Long = 0L,
#ColumnInfo(name = "tagId")
var tagId: Long = 0L
)
The data class I use for a return object in a Query:
data class NameWithTags(
#Embedded
val name: Name,
#Relation(
parentColumn = "nameId",
entityColumn = "tagId",
associateBy = Junction(value = TagInName::class)
)
val listOfTag : List<Tag>
)
This is how I query to get all NamesWithTags:
#Query("SELECT * FROM names_table")
#Transaction
fun getNamesWithTags() : LiveData<List<NameWithTags>>
So the thing I need to do is, I need to Query to return LiveData<List<NameWithTags>> where every NamesWithTags has a list which contains the Tag ID that I Query for.
From my interpretation of what you say you need to do, then :-
#Transaction
#Query("SELECT names_table.* FROM names_table JOIN tagInName_table ON names_table.nameId = tagInName_table.nameId JOIN tags_table ON tagInName_table.tagId = tags_table.tagId WHERE tags_table.tagId=:tagId ")
fun getNameWithTagsByTagId(tagId: Long): LiveData<List<NamesWithTags>>
Note the above is in-principle code and has not been compiled or tested, so it may contain some errors.
A NameWithTags will contain ALL related tags whcih should be fine according to (where every NamesWithTags has a list which contains the Tag ID ), if you wanted just certain Tags in the List of Tags then it's a little more complex, this is explained in a recent answer at Android Room query with condition for nested object
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>
I have two different entities. One has two references to the other one and I need to get a attribute of the reference.
my_main_table.primary_type is a foreign key of types._id and my_main_table.secondary_type is a foreign key of types._id that can be null.
Is a prepopulated database copied using RoomAsset library, so the scheme is already done in the database. Here is the diagram of the database:
Here is my main entity:
#Entity(
tableName = "my_main_table",
foreignKeys = [
ForeignKey(
entity = Type::class,
parentColumns = ["_id"],
childColumns = ["secondary_type"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT
),
ForeignKey(
entity = Type::class,
parentColumns = ["_id"],
childColumns = ["primary_type"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT
)
]
)
data class MainTable(
#PrimaryKey
#ColumnInfo(name = "_id", index = true)
val id: Int,
#ColumnInfo(name = "number")
val number: String,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "primary_type")
val primaryType: String,
#ColumnInfo(name = "secondary_type")
val secondaryType: String?
)
And here is my reference:
#Entity(tableName = "types")
data class Type(
#PrimaryKey
#ColumnInfo(name = "_id")
val id: Int,
#ColumnInfo(name = "name")
val name: String
)
Finally the SQL code for #Query:
SELECT p._id AS _id,
p.number AS number,
p.name AS name,
pt.name AS primary_type,
st.name AS secondary_type
FROM my_main_table p
INNER JOIN types pt ON p.primary_type == pt._id
LEFT JOIN types st ON p.secondary_type == st._id
What I want is to get the value of types.name throught the relation. But I can't figure out how. Should I need another method in my repository to get the value of the name?
Thanks.
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.