I've recentyly started using Room and I would like to know if when inserting or updating an Entitiy there's any way to check if the value is null, and in that case, do not insert it /update it.
What I want to do is something like
#Entity
data class Person{
#PrimaryKey
val id: Int,
val name:String
val surname:String
}
Would it be possible in a simple way to perform an #Update operation for those fields which are not null? and those which are null keep them as tey are?
For example in an update perhaps I might have informed the id and the name, but in another update I might have informed the id and the surname. So what I want is to merge the information, but if possible without having to make a select query to check the values stored.
I've read the following post, but my doubt then it would be, is it possible to define an #Entity, with all the fields defined as the one I mentioned before and then have other entities to just update some fields, something like:
#Entity(tableName = "person")
data class PersonUpdateSurname{
#PrimaryKey
val id: Int,
val name:String
}
#Entity(tableName = "person")
data class PersonUpdateSurname{
#PrimaryKey
val id: Int,
val surname:String
}
Is there a way to tell Room which is the original table structure?
Is there a way to tell Room which is the original table structure?
This question is not clear. Maybe there is some misunderstanding you have.
Try to follow next schema:
There should be only one Person-related data class annotated with Room-annotations - #Entity, #PrimaryKey and so on. In your case it is Person class.
All the rest mentioned auxiliary classes should be just POJO (plain data classes), since they are not being persisted. In your case - PersonName and PersoneSurname (with the fields you described but without Room's annotations).
In DAO use entity-parameter in #Update:
#Update(entity = Person::class)
fun updateName(personName: PersonName)
#Update(entity = Person::class)
fun updateSurname(personeSurname: PersonSurname)
In your Repository call method what you need. If you want to update only name - you use method updateName() and instance of PersonName class as a parameter, for only surname's update - method updateSurname() and instance of class PersonSurname.
Related
I'm trying to use a room entity with a value class:
#JvmInline
value class UserToken(val token: String)
and the entity:
#Entity(tableName = TABLE_AUTH_TOKEN)
data class TokenEntity(
#PrimaryKey val id: Int = 0,
val token: UserToken
)
I get the following error:
error: Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
public final class TokenEntity {
^
is it even possible to use room with value class? I couldn't find anything about this. thanks
See the comment from #CommonsWare. Android does not yet support value classes for Room.
The same holds true for the value classes introduced in kotlin 1.5. The type is not supported.
— Support Inline class in Room entity
Here is a possible explanation according to Kotlin Inline Classes in an Android World.
Looking to solve this you could try and add a TypeConverter for your Inline class, but since your Inline class is just the value it wraps when it’s compiled, this doesn’t make much sense and it doesn’t work as you’d expect even if you tried...
I’m just guessing it’s because this is a TypeConverter converting UserId to Int which is basically the same as Int to Int 😭. Someone will probably solve this problem, but if you have to create a TypeConverter for your Inline class then you are still plus one class for the count (multidex). 👎
I think yes if you can provide a type converter for it to change it to some sort of primitive data type (int , string, long ...etc) when it needs to be stored, and to change it back to its class type when it's fetched from database.
You can read about Type Converters from here
Referencing complex data using Room
other than that, your other class should be an entity and bind both your entities together using a Relation.
at least that's what I know about how to use Room.
UserToken always will have only one attribute? In this case, you don't need two classes, just use token: String directly on your entity class;
If you really need keep this class, you have two options:
TypeConverter, where you basically will convert the object into a json, and save as string in the database;
Relation, where you will transform the UserToken in a entity, and on TokenEntity save the tokenId.
I am at my wits end with a problem I cannot resolve - when trying to change the schema/data of a table in my database. I am pre-populating the database with Rooms .createFromAsset method.
Current schema and data (working) - as shown in DB Browser - this is the database used to Pre-Populate the apps database.
As I build the database with this code:
val instance = Room.databaseBuilder(
context.applicationContext,
MetarDatabase::class.java,
"metar_database"
)
.createFromAsset("database/metar_database.db")
.fallbackToDestructiveMigration()
.build()
And this data class:
#Entity(tableName = "airport_table")
data class Airport(
#PrimaryKey(autoGenerate = false)
val icao : String,
val name : String?,
val municipality : String?,
val scheduled : Boolean,
val iata : String?
)
It successfully pre-populates the database into the app: as seen on Database Explorer:
The problem is this: If I try and change the schema at all: adding columns, removing columns (done by changing a csv file and importing into a new table in DB Browser, then re-naming to airport_table and adding one to the database version), it does not load the data.
Eg: (Does NOT pre-populate):
#Entity(tableName = "airport_table")
data class Airport(
#PrimaryKey(autoGenerate = false)
val icao : String,
val name : String?
)
I get NO errors apart from "E/libc: Access denied finding property "ro.serialno"" which I don't think is relevant.
In addition - changing this one table means that my other table does not pre-populate.
However the empty database has still been made successfully, and I can add to it within the app.
Please help me - I've looked around stack overflow for similar questions. Some are similar, but involve getting anything to happen at all - my problem is it only works with one exact schema that I happen to succeed with whilst testing - but I want another schema.
Thanks a lot,
Liam
If you intend to export a file from the Room DB or access information using a database browser then use .setJournalMode(JournalMode.TRUNCATE) first. To ensure all data is fully preserved. I guess this is one of the possible causes of this error. Hope can help you
val instance = Room.databaseBuilder(
context.applicationContext,
MetarDatabase::class.java,
"metar_database"
)
.createFromAsset("database/metar_database.db")
.fallbackToDestructiveMigration()
.setJournalMode(JournalMode.TRUNCATE) // Add this line
.build()
After a long time I realised the problem.
I thought I did not need migration as I only wanted to change the pre-populating database, and not migrate anything currently on the device. However the use of destructive migration also deletes your pre-populating data when you add one to the schema number.
So simply add an empty migration and it works
val migration1: Migration = object : Migration(5, 6) { override fun migrate(database: SupportSQLiteDatabase) {}}
Thanks very much to myself for sorting my problem.
I was recently working on an Android app and ran into the error Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type) when adding Query's to my Room database repository class.
However, this took me quite a while to spot, because the error I got when building was, to my knowledge, quite unrelated. When searching SO for this question, I found a lot of answers which list something along the likes of 'Make sure you annotate your variables with #ColumnInfo' or 'Move your #Ignore tags to the body of the function' or 'Make an empty constructor like it says' but none of these were applicable to me or fixed the problem.
The weird thing was, my App ran fine if I just removed these three queries from my program, so it couldn't have anything to do with the constructor message.
Eventually, I noticed that my database Query was wrong (see comment in below code). This took me much longer than it should have though, since I had no clue my Query was causing a seemingly unrelated buildlog error.
I'm mostly posting this question here so that someone else might find it if they find themselves in a similar situation (ENSURE YOUR QUERIES ARE CORRECT!!), but I am also wondering if I'm understanding this error message wrong, or if it's actually a faulty error message.
Can someone explain to me why a faulty quert would result into said error message?
DAO containing faulty queries:
#Dao
interface GameItemDAO {
#Query("SELECT * FROM gameItemTable")
suspend fun getAllGameItems(): List<GameItem>
#Insert
suspend fun insertGameItem(gameItem: GameItem)
#Delete
suspend fun deleteGameItem(gameItem: GameItem)
#Query("DELETE FROM gameItemTable")
suspend fun deleteAllGameItems()
#Query("SELECT * FROM gameItemTable ORDER BY ID DESC LIMIT 1")
suspend fun getLastGameItem(): GameItem?
// These functions were faulty, and should have listed SELECT COUNT (*) FROM...
#Query("SELECT * FROM gameItemTable WHERE OUTCOME=0")
suspend fun getWinCount(): Int
#Query("SELECT * FROM gameItemTable WHERE OUTCOME=1")
suspend fun getLossCount(): Int
#Query("SELECT * FROM gameItemTable WHERE OUTCOME=2")
suspend fun getDrawCount(): Int
}
#Entity class:
#Entity(tableName = "gameItemTable")
data class GameItem(
#ColumnInfo(name = "date")
var date: Date,
#ColumnInfo(name = "moveComputer")
var moveComputer: MoveType,
#ColumnInfo(name = "movePlayer")
var movePlayer: MoveType,
#ColumnInfo(name = "outcome")
var outcome: OutcomeType,
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
var id: Long? = null
)
The error message is quite correct. Please note that when the return type of a query is not a collection/array/list of some type, Room will return the first item found, though it will retrieve all the data internally so it's not a good practice to depend on that behavior.
In your faulty case, you're asking Room to fetch * which means that it is expecting to return an object of a type with properties matching the 5 columns of the table. Also, it needs to know how to build it, either using a constructor with parameters matching the five columns or an empty constructor with setters for the five columns. Of course, Int doesn't match.
Till this time I was only doing sql queries which had only simple types or objects where annotation #Embedded was doing a job. Currently I have to extend my sql query to receive "simple data" in the list inside.
data class SimpleDataClass(
#Embedded val myObject: MyObject,
val id: UUID,
val listOfNumbers: List<MyNumbers>)
where
data class MyNumbers( val number: Int)
How to do this? By sql statement? Somehow with code? In DAO I return a:
LiveData<List<SimpleDataClass>>
When I try to receive data as before I get:
Entities and POJOs must have a usable public constructor. You can have an >empty constructor or a constructor whose parameters match the fields (by >name and type). - java.util.List
You provide very few information about your problem, but I think that Room relations is what you need. See this article Database relations with Room, should be helpful.
I'm using Room library to save and retrieve data for my application. The model class User is having variables like - Observable name etc where Observable is RxJava class. But now gradle build is failing due to - error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
So I'm looking how can we save Observable variable to table?
I'm aware of type converters but unable to get how to do this. Like save String value from Observable to Database then convert it again to Observable when we get data from Database.
Any hints please?
You can do somthing like
#Entity(tableName = "my_table")
public class MyTable {
#ColumnInfo(name = "my_string_value")
public String myStringValue;
}
Then create your DAO
#Dao
public class MyTableDao {
#Query("select my_string_value from my_table limit 1 ")
Flowable<String> selectMyStringValue();
}
Flowable<String> flowable = myAppDataBase.getMyTableDao().selectMyStringValue();
I don't know why you want to save an Observable... but first of all you shouldn't .
To use the code from above you have to add the RxJava2 dependency because Flowable.