Entity class must be annotated with #Entity - android

I'm having issues creating #Relation. I previously was using a complicated query to handle M:N, but I wanted to try the simpler #Relation and rely on ids. However, I get an error whenever I extend or embed the #Relation class. This works:
#Entity(tableName = "meta",
foreignKeys = [ForeignKey(
entity = FolderEntity::class,
parentColumns = ["id"],
childColumns = ["parentId"],
onDelete = CASCADE)],
indices = [
Index(value = ["uri"], unique = true),
Index(value = ["documentId"], unique = true),
Index(value = ["parentId"])])
open class MetadataEntity {
#PrimaryKey(autoGenerate = true)
var id: Long = 0
...
}
#TypeConverters(MetadataResult::class)
class MetadataResult : MetadataEntity() {
var keywords: List<String>? = null
var parentUri: String? = null
#TypeConverter
fun fromGroupConcat(keywords: String?): List<String>? = keywords?.split(",")
}
However, this does not work:
data class MetadataXmp(
#Embedded
val metadata:MetadataEntity,
#Relation(
parentColumn = "id",
entityColumn = "metaId",
projection = ["subjectId"],
entity = SubjectJunction::class)
var subjectIds: List<Long> = Collections.emptyList(),
#Relation(
parentColumn = "parentId",
entityColumn = "id",
projection = ["documentUri"],
entity = FolderEntity::class)
var parentUris: List<String> = Collections.emptyList())
Please note that the varying object type (class, data class, etc) is just the state of disarray of my attempts. I have tried various versions of extension with MetadataXmp or embedding, abstract, data class, etc. I don't seem to be able to introduce #Relation.

I was facing a similar Error message, for me the issue was that I had not declared the enity in the Database class but I had Annotated the pojo, Posting answer as it might help somebody with similar problem.

The error is actually related to #RawQuery:observedEntities. I was observing the result POJO instead of the underlying #Entity. The error message simply points to the POJO which is misleading. Google is looking at improving the error message.

I was facing the same issue while implementing the room DB in our application. but in my case room DB version is 2.2.6 when I increase to the latest version 2.3.0 solved my issue.

Related

What do #Relation classes mean when creating a one-to-many relationship in Room?

I'm making an Workout log app.
One Workout has multiple sets.
I want to store this in a one-to-many relationship in Room.
In conclusion, I succeeded in saving, but I'm not sure what one class does.
All of the other example sample code uses this class, so I made one myself, but it doesn't tell me what it means.
WorkoutWithSets
data class WorkoutWithSets(
#Embedded val workout: Workout,
#Relation (
parentColumn = "workoutId",
entityColumn = "parentWorkoutId"
)
val sets: List<WorkoutSetInfo>
)
The following two entity classes seem to be sufficient to express a one-to-many relationship. (Stored in Room)
Workout
#Entity
data class Workout(
#PrimaryKey(autoGenerate = true)
var workoutId: Long = 0,
var title: String = "",
var memo: String = "",
)
It seems that the following two entity classes are sufficient enough to store a one-to-many relationship.. (stored in Room)
WorkoutSetInfo
#Entity(
foreignKeys = [
ForeignKey(
entity = Workout::class,
parentColumns = arrayOf("workoutId"),
childColumns = arrayOf("parentWorkoutId"),
onDelete = ForeignKey.CASCADE
)
]
)
data class WorkoutSetInfo(
#PrimaryKey(autoGenerate = true)
val id: Long = 0,
val set: Int,
var weight: String = "",
var reps: String = "",
var unit: WorkoutUnit = WorkoutUnit.kg,
val parentWorkoutId: Long = 0
)
Even if the WorkoutWithSet class does not exist, the Workout and WorkoutSetInfo classes are stored in Room.
What does WorkoutWithSets class mean? (or where should I use it?)
What does WorkoutWithSets class mean?
It is a class that can be used to retrieve a Workout along with all the related WorkoutSetInfos via a simple #Query that just retrieves the parent Workouts.
What Room does is add an additional query that retrieves the children (WorkoutSetInfo's) for each Workout.
The result being a list of WorkOutWithSets each element (a Workout) containing/including a list of all the related WorkoutSetInfo's.
You would use this when you want to process a Workout (or many Workouts) along with the related WorkoutSetInfo's (aka the child WorkoutSetInfo's for the parent Workout).
What Room does is consider the type (objects) to be returned.
So if you had
#Query("SELECT * FROM workout")
fun getJustWorkouts(): List<Workout>
then the function would return just a list of Workout objects.
But if you had
#Query("SELECT * FROM workout")
fun getWorkoutsWithSets(): List<WorkoutWithSets>
then the function would return a list of WorkoutWithSets and thus the parent Workouts with the child WorkoutSetInfo's.
What Room does is build and execute an underlying query, for eack Workout extracted, along the lines of "SELECT * FROM workoutInfoSet WHERE workout.workoutId = parentWorkoutId" and hence why it suggests the use of the #Transaction annotation (the build will include a warning if #Transaction is not coded).

Many to many android room ref extra variable

I have a Many to Many relationship set up on my Room database denoted by the following diagram:
eveything works great but I would like to make the following addition:
My question is how do I go about having RecipeWithIngredients get this unit:String variable from the RecipeIngredientRef Entity when a recipeWithIngredients is constructed on a query?
#Entity(indices = [Index(value = ["name"],unique = true)])
data class Recipe(
var name: String,
val image: String?
) {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "recipeID")
var ID: Long = 0
}
#Entity(indices = [Index(value = ["name"],unique = true)])
data class Ingredient(
val name: String,
val image: String?,
val amount: Int
) {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "ingredientID")
var ID: Long = 0
}
#Entity(primaryKeys = ["recipeID", "ingredientID"])
data class RecipeIngredientRef(
val recipeID: Long,
val ingredientID: Long,
val unit: String
)
data class RecipeWithIngredients(
#Embedded
val recipe: Recipe,
#Relation(
parentColumn = "recipeID",
entity = Ingredient::class,
entityColumn = "ingredientID",
associateBy = Junction(
value = RecipeIngredientRef::class,
parentColumn = "recipeID",
entityColumn = "ingredientID"
)
)
val ingredients: List<Ingredient>
)
As far as I know there is no built-in way using current many-to-many Relations in Room for that. So I just want to save your time for researches.
This RecipeIngredientRef table is used internally just for two other tables' join and for now adding there additional fields will not help to get these fields with #Relations/Junction mechanism.
You can try workarounds, but they are no so elegant and they needed to dig deeper into Room's result's processing:
Don't use #Relation/Junction mechanism for RecipeWithIngridients at all, write your own query with two JOINs (since you should get data from 3 tables). As a result you'll get some raw set of records and then you should use loops and methods of Kotlin collections to turn result into needed form.
Use #Relation/Junction mechanism, but after getting result - make additional processing of result - make one more query to RecipeIngredientRef table and set missing unit value manually (again with loops and methods of Kotlin collections).

"no such column" SQL error from Android Room and multiple #Embedded fields on POJO

The original question is at the very bottom. I have created a minimal (non-)working example of my problem which is hopefully easier to read through. The example is here at gitlab. There is a readme describing the problem. I am pasting some parts of the project here.
The data model is plain simple:
Owner <--(1:N)-- Child --(N:1)--> ReferencedByChild
All I want to do is to read an Owner from the database with all its associated Child objects and for each Child object
also the ReferencedByChild object that it references.
The whole code that reproduces my problem is below. What I am not 100% sure about is the #Relation on the OwnerWithEverything POJO. See below please.
#Database(
entities = [
Owner::class,
Child::class,
ReferencedByChild::class
],
version = 1
)
abstract class AppDatabase : RoomDatabase() {
abstract fun appDao(): AppDao
}
#Dao
abstract class AppDao {
#Insert
abstract fun insertOwner(owner: Owner): Long
#Insert
abstract fun insertChild(child: Child): Long
#Insert
abstract fun insertReferencedByChild(referencedByChild: ReferencedByChild): Long
#Query("SELECT * FROM Child INNER JOIN ReferencedByChild ON Child.referencedByChildId = ReferencedByChild.refByChildId ORDER BY Child.childText")
abstract fun findAllChildrenWithReferencedClasses(): List<ChildWithReferenced>
// Commenting this query out makes the build pass, so something here is incorrect.
#Query("SELECT * FROM Owner")
abstract fun findOwnersWithEverything(): List<OwnerWithEverything>
}
// ENTITIES
#Entity
data class Owner(
#PrimaryKey(autoGenerate = true)
val ownerId: Long,
val ownerText: String
)
#Entity(
foreignKeys = [
ForeignKey(
entity = Owner::class,
parentColumns = arrayOf("ownerId"),
childColumns = arrayOf("referencedOwnerId"),
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = ReferencedByChild::class,
parentColumns = arrayOf("refByChildId"),
childColumns = arrayOf("referencedByChildId"),
onDelete = ForeignKey.CASCADE
)
]
)
data class Child(
#PrimaryKey(autoGenerate = true)
val childId: Long,
val childText: String,
val referencedOwnerId: Long,
val referencedByChildId: Long
)
#Entity
data class ReferencedByChild(
#PrimaryKey(autoGenerate = true)
val refByChildId: Long,
val refText: String
)
// POJOS
// The Child has exactly one ReferencedByChild reference. This POJO joins those two
class ChildWithReferenced(
#Embedded
var child: Child,
#Embedded
var referencedByChild: ReferencedByChild
)
class OwnerWithEverything {
#Embedded
var owner: Owner? = null
#Relation(
parentColumn = "ownerId",
entityColumn = "referencedOwnerId",
entity = Child::class // which entity should be defined here?
)
var childrenWithReferenced: List<ChildWithReferenced>? = null
}
Building this code results in this error message:
error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: refByChildId)
I think that the Owner query is badly constructed, but I am not entirely sure. If that is the problem, what is the correct way to construct the query?
This is the original question
I have a nested POJO structure that should represent a single Game having multiple Rounds and each Round has a single Topic associated with it:
class GameWithRounds {
#Embedded
var game: Game? = null
#Relation(
parentColumn = "id",
entityColumn = "gameId",
entity = RoundRoom::class
)
var rounds: List<RoundWithTopic>? = null
}
class RoundWithTopic(
#Embedded
var round: RoundRoom,
#Embedded(prefix = "topic_")
var topic: Topic
)
The embedded annotation on Topic specifies a prefix because there are clashing id properties.
The Room Query that can fetch those classes is:
#Query("SELECT Topic.id as topic_id, Topic.name as topic_name, (...), RoundRoom.* FROM RoundRoom INNER JOIN Topic ON RoundRoom.topicId = Topic.id")
abstract fun findRoundsWithTopics(): List<RoundWithTopic>
However, building the project gives me Room errors:
There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: topic_id)
Even though when I induce a warning about which fields are actually present, this is what Room tells me:
Columns returned by the query: topic_id, topic_name, topic_description, topic_language, topic_keywords, topic_sourceUrls, topic_furtherUrls, topic_questions, order, gameId, topicId, status, id. Fields in cz.melkamar.sklapecka.model.RoundWithTopic: order, gameId, topicId, status, id, topic_id, topic_name, topic_description, topic_language, topic_keywords, topic_sourceUrls, topic_furtherUrls, topic_questions, topic_image.
The topic_id column is there in the query result! Why am I getting this error?
For completeness, this is the entities:
#Entity
data class Game(
#PrimaryKey(autoGenerate = true)
val id: Long = 0,
#Embedded
val gameConfigurationEmbed: GameConfigurationEmbed
)
data class GameConfigurationEmbed(
var secondsPerTurn: Int,
var maxSecondsPerTurn: Int,
var bonusSecondsPerAnswer: Int
)
#Entity(
foreignKeys = [
ForeignKey(
entity = Game::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("gameId"),
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = Topic::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("topicId"),
onDelete = ForeignKey.CASCADE
)
]
)
#TypeConverters(RoomConverters::class)
data class RoundRoom(
val order: Int,
var gameId: Long,
val topicId: String,
var status: RoundStatus = RoundStatus.CREATED,
#PrimaryKey(autoGenerate = true)
val id: Long = 0
) {
enum class RoundStatus {
CREATED, UPCOMING, IN_PROGRESS, FINISHED
}
}
#Entity
data class Topic(
#PrimaryKey val id: String,
val name: String,
val description: String,
val language: String,
val keywords: List<String>,
val sourceUrls: List<String>,
val furtherUrls: List<String>,
val questions: List<String>,
val image: ByteArray?
)
After some research, specifically looking at this link : How can I represent a many to many relation with Android Room?
the only answers we found would be to
either create a hand-made sql query to handle this type of situation
where you have a many-to-many relationship
OR
to alternatively have an additional joining entity which gets
updated as the rest of the objects are updated. With this approach, you can get the ID's and then create additional queries as needed
It seems embedded fields and type converter is not properly used on observing the question. I don't want to go in detail in the solution of the question since it is trying to use complex relations and I cannot test it replicating in my machine.
But I want to provide insight on using Embedded fields and TypeConverter.
Let's take an example from the question above:
Game table has fields
id, secondsPerTurn, maxSecondsPerTurn, bonusSecondsPerAnswer.
It is okay to create entity like below.
#Entity
data class Game(
#PrimaryKey(autoGenerate = true)
val id: Long = 0,
#Embedded
val gameConfigurationEmbed: GameConfigurationEmbed
)
data class GameConfigurationEmbed(
var secondsPerTurn: Int,
var maxSecondsPerTurn: Int,
var bonusSecondsPerAnswer: Int
)
Here in the SQLite table, data is actually stored in four different columns but Room does CRUD operation based on data class structure giving higher feasibilty to the developers.
TypeConverter
Type converter will be helpful if we want to store non primitive data types or same type of data which will not be covered by #Embedded.
For example, a football game can be held in two places: home and away. Home and Away can have the same field names like placeName, latitude, longitude. In this case, we can create data class and type converters like below:
data class GamePlace(
val placeName:String,
val latitude:String,
val longitude:String
)
#Entity
data class Game(
#PrimaryKey(autoGenerate = true)
val id: Long = 0,
#Embedded
val gameConfigurationEmbed: GameConfigurationEmbed
#TypeConverters
var home: GamePlace? = null,
#TypeConverters
var away: GamePlace? = null,
)
object Converters {
private val gson = Gson()
#TypeConverter
#JvmStatic
fun fromGamePlace(gamePlace: GamePlace?): String? {
return if (gamePlace == null) null else gson.toJson(gamePlace)
}
#TypeConverter
#JvmStatic
fun toGamePlace(jsonData: String?): GamePlace? {
return if (jsonData.isNullOrEmpty()) null
else gson.fromJson(jsonData, object : TypeToken<GamePlace?>() {}.type)
}
}
While using type converter, converter class should be defined in database class as below:
#Database(
entities = [Game::class /* ,more classes here*/],
version = 1
)
#TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun gameDao(): GameDao
//.....
}
I hope this will help to deal with Embedded and TypeConverter in Room.

Fetching data from multiple tables in single transaction using Room?

Using Room I am trying to fetch data from two tables (Company and Farm) into a single list. Using commonsware answer I created a base class CompanyFarmBase and two child classes Company and Farm. Now using the example I created Dao class with following code:
#Query("SELECT * FROM farm_table")
fun getAllFarm(): List<UserModel.Farm>
#Query("SELECT * FROM company_table")
fun getAllCompany(): List<UserModel.Company>
#Transaction
fun getAllCompanyFarm(): List<UserModel.CompanyFarmBase> {
val result = ArrayList<UserModel.CompanyFarmBase>()
result.addAll(getAllCompany())
result.addAll(getAllFarm())
return result
}
Now when I try to build I get these errors:
dao/FarmDao_Impl.java:100: error: illegal start of expression
List<UserModel.CompanyFarmBase> _result = FarmDao.DefaultImpls.getAllCompanyFarm(this, );
dagger.internal.codegen.ComponentProcessor was unable to process com.navdeep.di.component.AppComponent because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
Please let me know where I went wrong. Querying separately each table gives proper data. Thanks!
Recently had this issue as well, passing in a default parameter will fix the issue. Why, no idea.
#Transaction
fun getAllCompanyFarm(notUsed: Boolean = true): List<UserModel.CompanyFarmBase> {
val result = ArrayList<UserModel.CompanyFarmBase>()
result.addAll(getAllCompany())
result.addAll(getAllFarm())
return result
}
create separate data class to chain that models.
data class TaskWithActivities(
#Embedded var taskEntity: TaskEntity, #Relation(
parentColumn = "taskId",
entityColumn = "taskpId"
) var activities: List<ActivityEntity>
)
inside room entity classes write relations:
for a task (parent)
#Entity(
tableName = "task_entity",
primaryKeys = ["taskId", "mandantId"]
)
#kotlinx.parcelize.Parcelize
data class TaskEntity(
for activity:
#Entity(
tableName = "activity_entity", indices = arrayOf(Index(value = ["taskpId", "mandantId"])),
foreignKeys = [
ForeignKey(
entity = TaskEntity::class,
parentColumns = ["taskId", "mandantId"],
childColumns = ["taskpId", "mandantId"],
onDelete = NO_ACTION
)],
primaryKeys = ["taskpId", "activityId", "mandantId"]
)
now you can request from two tables:
in your tasks DAO:
#Transaction
#Query("SELECT * FROM task_entity ORDER BY taskDueDateFinish ASC")
fun observeTaskWithActivities(): LiveData<List<TaskWithActivities>>

Room Database overriding inserted objects

I have my entity object this way:
#Entity(tableName = "woks", foreignKeys = arrayOf(ForeignKey(
entity = Order::class,
parentColumns = arrayOf("entryid"),
childColumns = arrayOf("order_id"),
onDelete = ForeignKey.CASCADE
)))
data class Wok(
val order_id: String
) {
#ColumnInfo(name = "id")
#PrimaryKey(autoGenerate = true)
var id: Long = 0
}
and the insertion in my DAO file defined this way:
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insertWok(wok: Wok): Long
As you can see the conflict is ignored so when ever I tried to insert the same object again it will override that object and keep increment the id.
So what I want to achieve is inserting the same object so many times
To insert your object many times, you need to change the primary key.
Room is based on it to know if it's a new object or not.
I have tried many combination which are possible with your given information, but i didn't came with anything as you are saying.
Can u please share more information like your contructor, how you are creating work object,
actually what happen with output (before-after)
This may be happen,
if your work object id is 0 while passing to your insertWork method
if u put 2 identical object with id 0 of both, because of autogenrate=true room will autogenrate id for them, and as room will create unique id, these object treat as different object.

Categories

Resources