Room one-to-many, to many - android

I am little a bit confused on how i should set up a data class consisting of two lists.
I have a data class looking like this
#Entity(tableName = "recipe")
data class Recipe(
#PrimaryKey(autoGenerate = false)
val recipeName: String,
//val keyIngrediens: List<KeyIngredient>,
//val steps: List<steps>
So for KeyIngrediens i have set it up like this and it works perfectly without problems
#Entity
data class KeyIngredients(
#PrimaryKey(autoGenerate = false)
val title: String,
val image: String,
val undertitle: Int,
val recipeName: String
)
data class RecipeWithkeyIngredients(
#Embedded val recipe: Recipe,
#Relation(
parentColumn = "recipeName",
entityColumn = "recipeName"
)
val keyIngredients: List<KeyIngredients>
)
So basically, i got it to work with one list and one object, but Im a bit confused as how i should handle it when i have two lists in a single object. Right now I'm calling RecipeWithKeyIngredients which returns the recipe object with the list of key ingredients, but i don't know how to make it also contain the steps list.
In short it should be like RecipeWithkeyIngredientsANDsteps if that is possible.

This is not enough for recipes. A recipe contains many ingredients, an ingredient belongs to many recipes. So it should be handled with many-to-many relationship.
#Entity
data class Recipe(
#PrimaryKey
val recipeId: Long,
val name: String
)
#Entity
data class KeyIngredient(
#PrimaryKey
val keyIngredientId: Long,
val title: String,
val image: String,
val undertitle: Int,
)
#Entity
data class Step(
#PrimaryKey
val stepId: Long,
val instruction: String,
val stepRecipeId: Long
)
#Entity(
primaryKeys = ["recipeId", "keyIngredientId"]
)
data class RecipeKeyIngredient(
val recipeId: Long,
val keyIngredientId: Long
)
Get the list of ingredients of a recipe
data class RecipeWithIngredients (
#Embedded
val recipe: Recipe,
#Relation(
parentColumn = "recipeId",
entity = KeyIngredient::class,
entityColumn = "ingredientId",
associateBy = Junction(
value = RecipeKeyIngredient::class,
parentColumn = "recipeId",
entityColumn = "keyIngredientId"
)
)
val keyIngredients: List<KeyIngredient>
)
Get the list of recipes with the same ingredient
data class IngredientWithRecipes(
#Embedded
val ingredient: Ingredient,
#Relation(
parentColumn = "ingredientId",
entity = Recipe::class,
entityColumn = "recipeId",
associateBy = Junction(
value = RecipeKeyIngredient::class,
parentColumn = "ingredientId",
entityColumn = "recipeId"
)
)
val recipes: List<Recipe>
)
Now you can query database for the result as:
#Dao
interface RecipeKeyIngredientDao {
#Query("SELECT * FROM Recipe")
fun getRecipeWithIngredients(): LiveData<List<RecipeWithIngredients>>
#Query("SELECT * FROM KeyIngredient")
fun getIngredientWithRecipes(): LiveData<List<IngredientWithRecipes>>
}
For the other part of the question, a recipe typically contains specific set of steps (instructions?) and to define the relationship between recipe and steps you will need nested relationship which defines one-to-many relationship between recipe and steps, and get the recipe from already modelled IngredientsOfRecipe instead of Recipe table which will give recipe with ingredients. Model the same as
data class RecipeWithIngredientsAndSteps(
#Embedded val recipe: IngredientsOfRecipe
#Relation(
entity = Step::class,
parentColumn = "recipeId",
entityColumn = "stepRecipeId"
)
val steps: List<Step>
)
Query using single transaction as follows
#Transaction
#Query("SELECT * FROM Recipe")
fun getRecipeWithIngredientsAndSteps(): List<RecipeWithIngredientsAndSteps>
Please refer to this

Assuming your Steps data class as follows:
#Entity
data class Steps(
#PrimaryKay
val stepName: String,
val duration: Int,
val recipeName: String,
val rank: Int
)
data class RecipeWithKeyIngredientsAndSteps(
#Embedded recipe: Recipe,
#Relation(
parentColumn = "recipeName",
entityColumn = "recipeName"
)
val keyIngredients: List<KeyIngredients>,
#Relation(
parentColumn = "recipeName",
entityColumn = "recipeName"
)
val steps: List<Steps>
)
At the end it's still a One-to-N Relationship

Related

Android Room Relation returns an invalid list

I have a query with a complex relation.
data class GameWithHouseworkResult(
#Embedded val game:GameClass,
#Relation(
entity = GameHouseworkResult::class,
parentColumn = "gameId",
entityColumn = "houseworkId" ,
associateBy = Junction(value=GameHouseworkResult::class)
)
val houseworkAndResult: List<HouseworkAndResult>
)
I have 3 entities and one additional class
#Entity
class GameClass(
#PrimaryKey(autoGenerate = true)
var gameId: Int = 0,
val mainPlayer:String,
var secondPlayer:String=""
}
#Entity
data class HouseworkClass(
#PrimaryKey
val houseworkId: Int,
val name:String,
val score:Int
) {
}
#Entity(primaryKeys = ["gameId", "houseworkId"])
class GameHouseworkResult(
var gameId: Long,
val houseworkId:Long,
val userToken:String,
val isDone:Boolean?= null) {
}
data class HouseworkAndResult(
#Embedded val gameHouseworkResult:GameHouseworkResult,
#Relation(
parentColumn = "houseworkId",
entityColumn = "houseworkId"
)
val housework: HouseworkClass
)
GameHouseworkResult is a binder class, it contains the following data
#Transaction
#Query("SELECT * FROM GameClass where gameId=:id")
suspend fun getGameWithHouseworkResult(id:Long): GameWithHouseworkResult
when I send a request with gameId = 26, I should get a list(houseworkAndResult) with five elements, but I get a list of 10 elements, and each one says that gameId = 26.
Most likely this is due to the same houseworkId, but I cannot understand where I went wrong when drawing up the relations. Please HELP me!
Don't worry, I've found a solution. I confused the #relation and used many-to-many instead of one-to-many. Correct code
data class GameWithHouseworkResult(
#Embedded val game:GameClass,
#Relation(
entity = GameHouseworkResult::class,
parentColumn = "gameId",
entityColumn = "gameId"
)
val houseworkAndResult: List<HouseworkAndResult>
)

How to query data in Android Room CrossReference entity for a special "many-to-many relationship" use case?

I followed the android dev guide to define Room many-to-many relationship:
https://developer.android.com/training/data-storage/room/relationships#many-to-many
data class Playlist(
#PrimaryKey val playlistId: Long,
val playlistName: String
)
#Entity
data class Song(
#PrimaryKey val songId: Long,
val songName: String,
val artist: String
)
#Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
val playlistId: Long,
val songId: Long
)
data class PlaylistWithSongs(
#Embedded val playlist: Playlist,
#Relation(
parentColumn = "playlistId",
entityColumn = "songId",
associateBy = #Junction(PlaylistSongCrossRef::class)
)
val songs: List<Song>
)
// Dao
#Transaction
#Query("SELECT * FROM Playlist")
fun getPlaylistsWithSongs(): List<PlaylistWithSongs>
The above works fine. However, I also want to save the sound property (i.e. Base volume...) of a song according to which playlist it belongs. For the same song, the volume is high when it is in "party" playlist and low when it is in "cool down" playlist. Similar for the same playlist, the volume is high for some songs and low for the rest.
So I changed the above PlaylistSongCrossRef and added the Dao as the following:
#Entity(table="playlistSongCrossRef", primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
val playlistId: Long,
val songId: Long,
val baseVolume: Double
)
data class PlaylistWithSongs(
#Embedded val playlist: Playlist,
#Relation(
parentColumn = "playlistId",
entityColumn = "songId",
associateBy = Junction(PlaylistSongCrossRef::class)
)
val songs: List<Song>
#Relation(
parentColumn = "playlistId",
entityColumn = "baseVolume",
associateBy = Junction(PlaylistSongCrossRef::class)
)
val baseVolumes: List<PlaylistSongCrossRef>
)
// Dao
#Query("SELECT * FROM Playlist WHERE playlistId = :playlistId ")
fun getPlaylistsWithSongs(playlistId: Int): List<PlaylistWithSongs>
However, the dao query does not work as expected. When the same baseVolume is stored in different song-playlistCrossRef pairs, the query will return duplicated entries.
I am not sure if this is the wrong query in dao or I need to re-design the Room DB structure. Thank you so much in advance!
data class PlaylistWithSongs(
#Embedded val playlist: Playlist,
#Relation(
parentColumn = "playlistId",
entityColumn = "songId",
associateBy = Junction(PlaylistSongCrossRef::class)
)
val songs: List<Song>
#Relation(
entity = PlaylistSongCrossRef::class,
parentColumn = "playlistId"
entityColumn = "playlistId"
)
val baseVolumes: List<PlaylistSongCrossRef>
)
Change the latter Relation to this and you will get the corresponding CrossRef list.

Room 2.5 nested relationships one to one many to one

Room
I have a relationship like this
puzzle -> has one-> dialogue -> has many -> dialogue lines
I followed the instructions on
https://developer.android.com/training/data-storage/room/relationships#kotlin
but still cant get it to work
These are the error that seem to be related:
error: Cannot find the child entity column puzzleDialogueId in com.example.puzzleherexamenandroid.data.room.databaseModels.DialogueWithLinesDatabase.
Tried the following constructors but they failed to match:
PuzzleWithDialogueDatabase(com.example.puzzleherexamenandroid.data.room.databaseModels.PuzzleDatabase,com.example.puzzleherexamenandroid.data.room.databaseModels.DialogueWithLinesDatabase) -> [param:puzzle -> matched field:puzzle, param:dialogueWithLines -> matched field:unmatched]C:\Users\Jasper\StudioProjects\PuzzleHerexamenAndroid\app\build\tmp\kapt3\stubs\debug\com\example\puzzleherexamenandroid\data\room\databaseModels\PuzzleWithDialogueDatabase.java:9: error: Cannot find setter for field.
PuzzleDatabaseDao.java:12: error: Type of the parameter must be a class annotated with #Entity or a collection/array of it.
java.util.List<com.example.puzzleherexamenandroid.data.room.databaseModels.PuzzleWithDialogueDatabase> puzzle);
these are my entities
#Entity(tableName = "puzzle_table")
#Parcelize
data class PuzzleDatabase (
#PrimaryKey
val puzzleId: Int,
val title: String,
val prompt: String,
val answer: String
): Parcelable
#Entity(tableName = "dialogue_table")
#Parcelize
data class DialogueDatabase (
#PrimaryKey
val dialogueId: Int,
val prompt: String,
val char1avatar: String,
val char2avatar: String,
val puzzleDialogueId : Int
): Parcelable
#Entity(tableName = "dialogueLine_table")
#Parcelize
data class DialogueLineDatabase (
#PrimaryKey
val dialogueLineId: Int,
val line: String,
val speaking: Int,
val dialogueForeignkeyId: Int
): Parcelable
these are the classes for the relationship
data class PuzzleWithDialogueDatabase(
#Embedded val puzzle : PuzzleDatabase,
#Relation(
entity = DialogueWithLinesDatabase::class,
parentColumn = "puzzleId",
entityColumn = "puzzleDialogueId"
)
val dialogueWithLines: DialogueWithLinesDatabase
)
data class DialogueWithLinesDatabase(
#Embedded val dialogue:DialogueDatabase,
#Relation(
parentColumn = "dialogueId",
entityColumn = "dialogueForeignkeyId"
)
val dialogueLines: List<DialogueLineDatabase>
)
And this is my doa
#Dao
interface PuzzleDatabaseDao {
#Transaction
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insertAll(puzzle: List<PuzzleWithDialogueDatabase>)
#Transaction
#Query("SELECT * from puzzle_table ORDER BY puzzleId DESC")
fun getAllPuzzles(): LiveData<List<PuzzleWithDialogueDatabase>>
}
I guess you should change entity DialogueWithLinesDatabase with DialogueDatabase inside your PuzzleWithDialogueDatabase class' Releation definition:
data class PuzzleWithDialogueDatabase(
#Embedded val puzzle : PuzzleDatabase,
#Relation(
entity = DialogueDatabase::class, // <- changed
parentColumn = "puzzleId",
entityColumn = "puzzleDialogueId"
)
val dialogueWithLines: DialogueWithLinesDatabase
)
The entity property in #Relation should mention the database entity and not the relation-class. Try:
data class PuzzleWithDialogueDatabase(
#Embedded val puzzle : PuzzleDatabase,
#Relation(
entity = DialogueDatabase::class,
parentColumn = "puzzleId",
entityColumn = "puzzleDialogueId"
)
val dialogueWithLines: DialogueWithLinesDatabase
)

Room #Relation with nested dynamic WHERE clause

Suppose I have items which can each have many categories:
#Entity(tableName = "items")
data class Item(
#PrimaryKey val item_id: Long,
val external_id: String,
val name: String,
val price: Long,
val image: String?,
var indexInResponse: Int = -1
)
#Entity(tableName = "categories")
data class Category(
#PrimaryKey val cat_id: Long,
val name: String,
val image: String?,
var indexInResponse: Int = -1
)
//-------
#Entity(tableName = "items_with_categories", primaryKeys = ["item", "cat"], indices = [Index(value = ["cat"])])
data class ItemCategoryCrossRef(
val item: Long,
val cat: Long
)
data class ItemWithCategories(
#Embedded val item: Item,
#Relation(
parentColumn = "item_id",
entityColumn = "cat_id",
associateBy = Junction(
ItemCategoryCrossRef::class,
parentColumn = "item",
entityColumn = "cat")
)
val cats: List<Category>
)
And I can retrieve all items with categories using this, which works well:
#Transaction
#Query("SELECT * FROM items ORDER BY indexInResponse ASC")
abstract fun getItemsWithCategories(): DataSource.Factory<Int, ItemWithCategories>
How can I retrieve items with particular categories based on ID using Room or SQL?
#Transaction
#Query("SELECT * FROM items WHERE ??<something>?? LIKE :catID ORDER BY indexInResponse ASC")
abstract fun getItemsWithCategoriesByCatID(catID: Long): DataSource.Factory<Int, ItemWithCategories>
I don't think this is possible, obviously the list of Categories is a list of POJOs, and it's a nested relation. So is the only way to do this in SQL to do it with a raw query?
This is the closest question I have found, but the DatabaseView in the answer here uses a hard coded field for it's WHERE clause and I need to be able to specify the category ID at run time.

Something I am missing with many to many relationship in android room database

Following the official documentation for using #Relation and Junction tables for cross relationship between two tables and I am not having good results.
I am using these two entities:
#Entity(tableName = "Dog",
foreignKeys = arrayOf(
ForeignKey(entity = Owner::class,
parentColumns = arrayOf("ownerId"),
childColumns = arrayOf("dogOwnerId"))
))
data class Dog(
#PrimaryKey val dogId: Long,
val dogOwnerId: Long,
val name: String,
val cuteness: Int,
val barkVolume: Int,
val breed: String
)
#Entity
data class Owner(#PrimaryKey val ownerId: Long, val name: String)
using this table to join them
#Entity(primaryKeys = ["dogId", "ownerId"])
data class DogOwnerCrossRef(
val dogId: Long,
val ownerId: Long
)
and this class for the Query response:
data class DogWithOwners(
#Embedded val dog: Dog,
#Relation(
parentColumn = "dogId",
entity = Owner::class,
entityColumn = "ownerId",
associateBy = Junction(DogOwnerCrossRef::class)
)
val owners: List<Owner>
)
My DAO:
#Transaction
#Query("SELECT * FROM Dog")
fun getdogWithOwners(): List<DogWithOwners>
the result is always null for Owner objects which looks like the #Transaction haven't been made,I am puzzled on how to handle this query without making any join queries.

Categories

Resources