I have the typical one-to-one relationship between two tables
One entity/table has documents in it
#Entity(
tableName = "documents",
foreignKeys = [
(ForeignKey(
entity = Document::class,
parentColumns = ["id"],
childColumns = ["parentId"],
onDelete = ForeignKey.CASCADE
)),
(ForeignKey(
entity = Attachment::class,
parentColumns = ["id"],
childColumns = ["attachmentId"],
onDelete = ForeignKey.CASCADE
))
],
indices = [
Index(value = ["parentId"], unique = true),
Index(value = ["attachmentId"])
]
)
class Document(
#PrimaryKey(autoGenerate = true)
val id: Long = 0,
#Nullable
val parentId: Long? = null,
val title: String,
val description: String? = "",
#Nullable
var attachmentId: Long? = null
)
and then I have this for attachments
#Entity(tableName = "attachments")
class Attachment(
#PrimaryKey(autoGenerate = true)
val id: Long,
val type: FileType = FileType.RAW,
val title: String? = "",
#ColumnInfo(typeAffinity = ColumnInfo.BLOB)
val data: ByteArray? = null
)
enum class FileType {
RAW, PDF, IMAGE
}
My DAO for documents is this:
#Dao
interface DocumentDao {
#Query("SELECT * FROM documents")
fun getAll(): LiveData<List<Document>>
#Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun insert(doc: Document)
#Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun insert(attachment: Attachment): Long
#Transaction
suspend fun insert(doc: Document, attachment: Attachment) {
val id = insert(attachment)
doc.attachmentId= id
insert(doc)
}
#Update
suspend fun update(vararg doc: Document?)
#Update
suspend fun update(vararg attachment: Attachment?)
#Transaction
suspend fun update(doc: Document, attachment: Attachment) {
if (attachment.id == 0L) {
val id = insert(attachment)
doc.attachmentId= id
} else {
update(attachment)
}
update(doc)
}
#Delete
suspend fun delete(doc: Document)
#Delete
suspend fun delete(attachment: Attachment)
}
I can add documents with attachments, update them, add child documents (for which I use the parentId foreign key).
My problem is that attachments are not deleted when parent documents are deleted, even if I can clearly see in the table that the id of the attachment has been correctly added. Child documents, referenced by parentId are deleted in accordance to onDelete = ForeignKey.CASCADE rule but attachments are not.
The generated SQL that creates the table looks like this:
CREATE TABLE `documents` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `parentId` INTEGER, `title` TEXT NOT NULL, `description` TEXT, `attachmentId` INTEGER, FOREIGN KEY(`parentId`) REFERENCES `documents`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`attachmentId`) REFERENCES `attachments`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )
I really do not understand why one foreign key (parentId) works when deleting parent rows but the other one (attachmentId) does not
Let's say you have:
Table Attachment, one row with id = 1, second with id = 2
Table Document, row with id = 1, attachmentId = 2.
As I understood, you want after deleting row in Document table - row in Attachment table with id = 2 to be deleted as well. But for that you have to change your db structure. Instead add foreign key documentId to Attachment table.
#Entity(tableName = "attachments",
foreignKeys = [
(ForeignKey(
entity = Document::class,
parentColumns = ["id"],
childColumns = ["documentId"],
onDelete = ForeignKey.CASCADE
)]
)
class Attachment(
#PrimaryKey(autoGenerate = true)
val id: Long,
val documentId: Long, // <-- Foreign key to Document Entity
val type: FileType = FileType.RAW,
val title: String? = "",
#ColumnInfo(typeAffinity = ColumnInfo.BLOB)
val data: ByteArray? = null
)
Since while deleting some row Sqlite looks all foreign keys in all other tables that are binded to that row and then deletes them.
UPDATE: Addition below is misleading and shouldn't be taken into account
And, in addition, are you sure your child rows really were deleted after deleting parent one? It seems you have to change parentColums and childColumns
ForeignKey(
entity = Document::class,
parentColumns = ["id"], // <-- should be childColumns instead?
childColumns = ["parentId"], // <-- should be parentColumns instead?
onDelete = ForeignKey.CASCADE
))
I ended up removing the foreign key for attachments and deleting them in a transaction
#Query("DELETE FROM attachments WHERE id = :attachmentId")
fun deleteAttachment(attachmentId: Long)
#Transaction
suspend fun deleteWithAttachment(doc: Document) {
if (doc.attachmentId != null) {
deleteAttachment(doc.attachmentId!!)
}
delete(doc)
}
Related
To prevent the deletion of a parent row which has one or more related child rows in my Room database, I've set my ForeignKey onDelete method to RESTRICT.
My database has two tables: products and document_products which has the ForeignKey on products, during the application usage, the user is able to delete all items from the products table but I need to still keep the items in document_products but the RESTRICT seems not to be working as even with it I'm getting:
FOREIGN KEY constraint failed (code 1811 SQLITE_CONSTRAINT_TRIGGER)
My DocumentProduct entity looks like this:
#JsonClass(generateAdapter = true)
#Entity(
tableName = "document_products",
foreignKeys = [
ForeignKey(
entity = Document::class,
parentColumns = ["id"],
childColumns = ["documentId"],
onDelete = CASCADE
),
ForeignKey(
entity = Product::class,
parentColumns = ["products_id"],
childColumns = ["document_products_productIdMap"],
onDelete = RESTRICT
)
],
indices = [Index("document_products_productIdMap"), Index("documentId"), Index(
value = ["document_products_productIdMap", "documentId", "labelType"],
unique = true
)]
)
data class DocumentProduct(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "document_products_id")
var id: Long,
#ColumnInfo(name = "document_products_productIdMap")
var productId: String,
#ColumnInfo(name = "document_products_quantity")
var quantity: Float,
var orderQuantity: Float,
#ColumnInfo(name = "document_products_purchase")
var purchase: Float,
var documentId: Long,
var labelType: String?,
var timestamp: Long?
)
While Product:
#Entity(tableName = "products")
open class Product(
#PrimaryKey(autoGenerate = false)
#ColumnInfo(name = "products_id")
open var id: String,
open var description: String?,
#ColumnInfo(defaultValue = "PZ")
open var unitOfMeasure: String,
#ColumnInfo(name = "products_purchase")
open var purchase: Float,
open var price: Float,
#ColumnInfo(name = "products_quantity")
open var quantity: Float
)
And in the application settings the user is able to run the following query from ProductDAO:
#Query("DELETE FROM products")
suspend fun deleteAll(): Int
What I'm looking for is a solution in which I can keep the parent rows which has one or more related child rows OR where I can keep the ForeignKey without a real relation.
RESTRICT is working as intended, it is not meant to exit quietly and leave things as before. Rather it is used to immediately exit rather than at the end of the current statement as per:-
RESTRICT: The "RESTRICT" action means that the application is prohibited from deleting (for ON DELETE RESTRICT) or modifying (for ON UPDATE RESTRICT) a parent key when there exists one or more child keys mapped to it. The difference between the effect of a RESTRICT action and normal foreign key constraint enforcement is that the RESTRICT action processing happens as soon as the field is updated - not at the end of the current statement as it would with an immediate constraint, or at the end of the current transaction as it would with a deferred constraint. Even if the foreign key constraint it is attached to is deferred, configuring a RESTRICT action causes SQLite to return an error immediately if a parent key with dependent child keys is deleted or modified.
https://www.sqlite.org/foreignkeys.html#fk_actions
You could simply not use Foreign Keys they are not mandatory for a relationship to exist. They are to enforce referential integrity.
An alternative approach with referential integrity would be to have the Products and DocumentsProducts independent relationships wise (i.e. drop the Foregin Keys and the productId column) and to then have an table for any relationships this catering for a many-many relationship between Products and DocumentProducts (which inherently supports 1-many and 1-1).
Such a table (a mapping table/crossref table/associative table ....) would have 2 columns one for the reference/map/association with the Product, the other for the DocumentProduct. You could have 2 Foreign Keys and also you could CASCADE for when a deletion happens.
The Delete (and update if coded) would CASCADE to this table not to the Product or the DocumentProduct, thus just removing cross reference between the two.
The Primary Key would be a composite of the two columns, you would have to use the primaryKey parameter of the #Entity annotation to define this.
The following code is along the lines of what would suit:-
#Entity(
tableName = "document_products",/*
foreignKeys = [
ForeignKey(
entity = Document::class,
parentColumns = ["id"],
childColumns = ["documentId"],
onDelete = CASCADE
),
ForeignKey(
entity = Product::class,
parentColumns = ["products_id"],
childColumns = ["document_products_productIdMap"],
onDelete = RESTRICT
)
],*/
indices = [/*Index("document_products_productIdMap"),*/ Index("documentId"), Index(
value = [/*"document_products_productIdMap",*/ "documentId", "labelType"],
unique = true
)]
)
data class DocumentProduct(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "document_products_id")
var id: Long,
//#ColumnInfo(name = "document_products_productIdMap")
//var productId: String,
#ColumnInfo(name = "document_products_quantity")
var quantity: Float,
var orderQuantity: Float,
#ColumnInfo(name = "document_products_purchase")
var purchase: Float,
var documentId: Long,
var labelType: String?,
var timestamp: Long?
)
#Entity(tableName = "products")
open class Product(
#PrimaryKey(autoGenerate = false)
#ColumnInfo(name = "products_id")
open var id: String,
open var description: String?,
#ColumnInfo(defaultValue = "PZ")
open var unitOfMeasure: String,
#ColumnInfo(name = "products_purchase")
open var purchase: Float,
open var price: Float,
#ColumnInfo(name = "products_quantity")
open var quantity: Float
)
#Entity(
primaryKeys = ["productIdMap","documentProductIdMap"],
foreignKeys = [
ForeignKey(
entity = Product::class,
parentColumns = ["products_id"],
childColumns = ["productIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = DocumentProduct::class,
parentColumns = ["document_products_id"],
childColumns = ["documentProductIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class ProductDocumentProductMap(
val productIdMap: Long,
#ColumnInfo(index = true)
var documentProductIdMap: Long
)
note commenting out code has been used to indicate code that isn't needed or must be changed to suit.
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 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 am working with room persistence library in android, i would appreciate if someone can help me in using foreign key, how to get data by using foreign key.
Just to summarize the above posts for future readers:
The foreign key syntax in Kotlin is
#Entity(foreignKeys = arrayOf(ForeignKey(entity = ParentClass::class,
parentColumns = arrayOf("parentClassColumn"),
childColumns = arrayOf("childClassColumn"),
onDelete = ForeignKey.CASCADE)))
The foreign key syntax in Java is:
#Entity(foreignKeys = {#ForeignKey(entity = ParentClass.class,
parentColumns = "parentClassColumn",
childColumns = "childClassColumn",
onDelete = ForeignKey.CASCADE)
})
Note: foreignKeys is an array, so in Java enclose #ForeignKey elements in { and }
You can refer to the official documentation for more information.
https://developer.android.com/reference/androidx/room/ForeignKey
Here how you can define and access a One-to-many (Foreign Key) relationship in Android Jetpack Room. Here the Entities are Artist and Album and the foreign key is Album.artist
#Entity
data class Artist(
#PrimaryKey
val id: String,
val name: String
)
#Entity(
foreignKeys = [ForeignKey(
entity = Artist::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("artist"),
onDelete = ForeignKey.CASCADE
)]
)
data class Album(
#PrimaryKey
val albumId: String,
val name: String,
#ColumnInfo(index = true)
val artist: String
)
then the embedded object (read official Android documentation: Define relationships between objects)
data class ArtistAndAlbums(
#Embedded
val artist: Artist,
#Relation(
parentColumn = "id",
entityColumn = "artist"
)
val albums: List<Album>
)
And finally the DAO
#Dao
interface Library {
#Insert
suspend fun save(artist: Artist)
#Insert
suspend fun save(vararg album: Album)
#Transaction
#Query("SELECT * FROM artist")
suspend fun getAll(): List<ArtistAndAlbums>
#Transaction
#Query("SELECT * FROM artist WHERE id = :id")
suspend fun getByArtistId(id: String): ArtistAndAlbums
}
trying this out, (all these operations happens in a Coroutine)
// creating objects
val artist = Artist(id="hillsongunited", name="Hillsong United" )
val artist2 = Artist(id="planetshakers", name="Planet Shakers" )
val album1 = Album(albumId = "empires", name = "Empires", artist = artist.id)
val album2 = Album(albumId = "wonder", name = "Wonder", artist = artist.id)
val album3 = Album(albumId = "people", name = "People", artist = artist.id)
val album4 = Album(albumId = "rain", name = "Rain", artist = artist2.id)
val album5 = Album(albumId = "itschristmas", name = "Its Christmas", artist = artist2.id)
val album6 = Album(albumId = "overitall", name = "Over It All", artist = artist2.id)
// saving to database
SongDatabase.invoke(applicationContext).library().save(artist)
SongDatabase.invoke(applicationContext).library().save(artist2)
SongDatabase.invoke(applicationContext).library().save(album1, album2, album3, album4, album5, album6)
Logging out all Artists
val all = SongDatabase.invoke(applicationContext).library().getAll()
Log.d("debug", "All Artists $all ")
D/debug: All Artists [ArtistAndAlbums(artist=Artist(id=hillsongunited, name=Hillsong United), albums=[Album(albumId=empires, name=Empires, artist=hillsongunited), Album(albumId=wonder, name=Wonder, artist=hillsongunited), Album(albumId=people, name=People, artist=hillsongunited)]), ArtistAndAlbums(artist=Artist(id=planetshakers, name=Planet Shakers), albums=[Album(albumId=rain, name=Rain, artist=planetshakers), Album(albumId=itschristmas, name=Its Christmas, artist=planetshakers), Album(albumId=overitall, name=Over It All, artist=planetshakers)])]
Logging out albums by a specific artist,
val hillsongAlbums = SongDatabase.invoke(applicationContext).library().getByArtistId(artist.id)
Log.d("debug", "Albums by artist ID: $hillsongAlbums ")
D/debug: Albums by artist ID: ArtistAndAlbums(artist=Artist(id=hillsongunited, name=Hillsong United), albums=[Album(albumId=empires, name=Empires, artist=hillsongunited), Album(albumId=wonder, name=Wonder, artist=hillsongunited), Album(albumId=people, name=People, artist=hillsongunited)])
#ForeignKey annotations are not used to define relations when getting data but to define relations when modifying data. To get relational data from a Room databse, Google recommends the #Relation along with the #Embedded annotation.
You can check out my answer here for more explanation if you're interested.