how to set autogenerate id in room database - android

In the room database I want to auto generate id, I did the bellow code but I get UNIQUE constraint failed error.
How can i get the table to autogenerate id and I do not want to pass id
#Entity
data class OfflineDatax (
#PrimaryKey(autoGenerate = true) val uid: Int = 0,
#ColumnInfo(name = "requestJSON") val requestJSON: String?,
#ColumnInfo(name = "requestCode") val requestCode: String?
)
#Dao
interface OfflineDataDao {
#Query("SELECT * FROM offlinedata")
fun getOfflineData(): Flow<List<OfflineData>>
#Insert()
suspend fun insertOfflineData(offlineData: OfflineData)
}
This is how inserting data
libOfflineDataDao.insertOfflineData(OfflineData(1,"test", "test"))
Thanks
R

we need the unique ID only to access the data but since you are just inserting the data and also you have set the default value as 0 to be inserted, Kotlin is smart enough to insert the data with just the rest two of the parameters. Please let me know if this has helped in any way.
libOfflineDataDao.insertOfflineData(OfflineData("test", "test"))
This should work too.

Related

Kotlin Room SUM operation

I need to perform column sum operation
But I don't understand what exactly I need to insert into fun getSumItem(): Flow<Cursor>
this is my Dao
#Dao
interface Dao {
#Insert
fun InsertItem(item: Income)
#Query("SELECT SUM(sum) AS value FROM income")
fun getSumItem(): Flow<Cursor>
}
this is my table
#Entity(tableName = "income")
data class Income(
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
#ColumnInfo(name = "date")
var date: String,
#ColumnInfo(name = "type")
var type: String,
#ColumnInfo(name = "sum")
var sum: Float,
)
I did not find a similar example and any detailed information on the Internet.
when I use Cursor i get this 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). - android.database.Cursor
all other options that I found on the Internet also gave errors
When you say SELECT SUM(sum) AS value FROM income the output that Room handles will be a Cursor with a single column named value. Room does not know how to extract this value and build a Cursor object from that single value.
Room expects to extract data from the underlying Cursor that is generated by the SQLite API, which Room is a wrapper to/around, and deliver the data according to the object that the function returns (and thus must be able to ascertain how to build the resultant object).
As Room extracts a single value then you simply need to retrieve the value returned as a Float.
fun getSumItem(): Flow<Float>

How to ignore primary key when I insert room database entity

How do I ignore the primary key when I insert some entity?
Room Entity has to have more than 1 primary key.
For example, there is an entity following under.
#Entity(primaryKeys = ["id"], tableName = "someEntity")
data class SomeEntity(
val id: Int = 0,
val someClass: SomeClass<*>? = null
)
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(obj: SomeClass): Completable
Parameter "obj" will have two column(fields).
When I have insert logic like that,
do I have to care about id (with autoGenerate annotation) column?
When I insert SomeEntity with dao,
I can only get SomeClass<*>? type, without id: Int.
Does #AutoGenerate annotation on column id can solve my problem?
Room understands val id: Int = 0 if it is marked with #PrimaryKey as a value to be ignored when inserting.
#Entity
data class SomeEntity(
#PrimaryKey(autoGenerate = true)
val id: Int = 0,
val someClass: SomeClass<*>? = null
)
and when creating a new instance SomeEntity(someClassInstance) is completely fine.
Note: if SomeClass isn't a basic class that SQL is able to save, you need to have some way to serialize it.

update multiple tables in Android Room Database

I have two models
Types Model
#Entity(tablename="tbl_types")
data class Types(
#PrimaryKey(autoGenerate = true)
val typesId:Int,
val name: String?,
val posts:List<Post>?))
PostModel
#Entity(tablename="tbl_post")
data class Post(
#PrimaryKey(autoGenerate = true)
val postId:Int,
val name: String?,
val details:String?))
A post can be a part of different types so when I change the post's data like name or details I want to update all the types that have this specific post in its posts column. How do I do that?. Do I have to update the type table manually? I don't quite know how the foreign key works and if it will let me update the item on the list or not.
Ok Room support Embedded objects in an entity so lets make Post embedded in Type like this:
#Entity(tablename="tbl_types")
data class Types(
#PrimaryKey(autoGenerate = true)
val typesId:Int,
val name: String?,
#Embedded val posts:List<Post>?))
now for updating the post you easily just change the desired post in the list, in the Type Model and you just need to say :
#Update
fun updateType(type: Types)
and that's the power of Room for updating a row easily.
Hope it Helps.
Not containing Post in Type table. Create third table to save the relateion of post and type.
#Entity(tablename="tbl_types")
data class Types(
#PrimaryKey(autoGenerate = true)
val typesId:Int,
val name: String?)
#Entity(tablename="tbl_post")
data class Post(
#PrimaryKey(autoGenerate = true)
val postId:Int,
val name: String?,
val details:String?))
#Entity(tablename="type_post_relation", primaryKeys =
["postId","typeId"])
data class PostTypeRelation(
val postId:Int,
val typeId: Int,
)
...
#Query("UPDATE Tour SET typeId = :toTypeId WHERE postId = :postId and typeId = : fromTypeId")
fun updatePostType(postId: Int, fromTypeId: Int, toTypeId: Int)
The relation table uses postId and typeId to be primary key. When changing post type, just update relation table with a postId and typeId. And if you want to query all posts in one specific type, you can use sql to combine query, so querying post's types.

Room Livedata returns incorrect values

I have an audio recorder app, where I enable the user to mark certain points in his recordings with predefined markers. For that purpose I have a MarkerEntity, which is the type of Marker, and a MarkTimestamp, the point at which the user marks a given recording. These entities are connected via a Relation, called MarkAndTimestamp.
#Entity(tableName = "markerTable")
data class MarkerEntity(
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "markerName") val markerName: String
)
#Entity(tableName = "markerTimeTable")
data class MarkTimestamp(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "mid") val mid: Int,
#ColumnInfo(name = "recordingId") val recordingId: Int,
#ColumnInfo(name = "markerId") val markerId: Int,
#ColumnInfo(name = "markTime") val markTime: String
)
data class MarkAndTimestamp(
#Embedded val marker: MarkerEntity,
#Relation(
parentColumn = "uid",
entityColumn = "markerId"
)
val markTimestamp: MarkTimestamp
)
The insertion of this data works flawlessly, I checked this via DB Browser for SQLite and Android Debug Database. The problem arises, when I want to display all marks for a recording. I fetch the entries with the following SQL statement.
#Transaction
#Query("SELECT * FROM markerTimeTable INNER JOIN markerTable ON markerTimeTable.markerId=markerTable.uid WHERE markerTimeTable.recordingId = :key")
fun getMarksById(key: Int): LiveData<List<MarkAndTimestamp>>
What ends up happening is, that if the user uses a Marker more than once, all marks created with that Marker have the same MarkerTimestamp row attached to them, specificially, the last row to be inserted with that Marker. The weird thing is, this only happens in the app using Livedata. Using the same query in DB Browser for SQLite returns the correct and desired data.
This is the stored data (correct)
MarkTimestamps
MarkerEntities
And this is the Livedata returned at this point (incorrect)
[
MarkAndTimestamp(marker=MarkerEntity(uid=1, markerName=Mark), markTimestamp=MarkTimestamp(mid=6, recordingId=2, markerId=1, markTime=00:05)),
MarkAndTimestamp(marker=MarkerEntity(uid=2, markerName=zwei), markTimestamp=MarkTimestamp(mid=5, recordingId=2, markerId=2, markTime=00:03)),
MarkAndTimestamp(marker=MarkerEntity(uid=1, markerName=Mark), markTimestamp=MarkTimestamp(mid=6, recordingId=2, markerId=1, markTime=00:05))
]
I also get the following build warning
warning: The query returns some columns [mid, recordingId, markerId, markTime] which are not used by de.ur.mi.audidroid.models.MarkAndTimestamp. You can use #ColumnInfo annotation on the fields to specify the mapping. You can suppress this warning by annotating the method with #SuppressWarnings(RoomWarnings.CURSOR_MISMATCH). Columns returned by the query: mid, recordingId, markerId, markTime, uid, markerName. Fields in de.ur.mi.audidroid.models.MarkAndTimestamp: uid, markerName. - getMarksById(int) in de.ur.mi.audidroid.models.MarkerDao
Why does Room return the wrong data and how do I fix this?
So, I still don't know what caused the behaviour described in my original post. My guess is, that the realtion data class and the SQL query interfered in some way, producing the cinfusing and incorrect outcome.
I solved my problem nonetheless. I needed to change
data class MarkAndTimestamp(
#Embedded val marker: MarkerEntity,
#Relation(
parentColumn = "uid",
entityColumn = "markerId"
)
val markTimestamp: MarkTimestamp
)
to
data class MarkAndTimestamp(
#Embedded val marker: MarkerEntity,
#Embedded val markTimestamp: MarkTimestamp
)
This makes sure, that all fields returned by the query are included in the data class.

Is it possible in Room to ignore a field on a basic update

I have the following entity:
#Entity
class Foo(
#PrimaryKey
#ColumnInfo(name = "id")
val id: Long,
#ColumnInfo(name = "thing1")
val thing1: String,
#ColumnInfo(name = "thing2")
val thing2: String,
#ColumnInfo(name = "thing3")
val thing3: String,
#ColumnInfo(name = "thing4")
val thing4: String
) {
#ColumnInfo(name = "local")
var local: String? = null
}
Where local is information that is not stored on the server, only local to the phone.
Currently when I pull information from the server GSON auto fills in my values, but since "local" does not come from the server it is not populate in that object.
Is there a way that when I call update I can have Room skip the update for the "local" column without writing a custom update to insert into all other columns except "local"? The pain point is that I could have many columns and each new column I add, I would have to add that to the custom insert statement.
I have also thought of a one-to-one mapping from the server entity to a new "local" entity, however now I have to deal with the pain of a join statement everywhere I get my entity since I need the local information.
I was hoping that I could do something like this:
#Entity
class Foo(
#PrimaryKey
#ColumnInfo(name = "id")
val id: Long,
#ColumnInfo(name = "thing1")
val instructions: String,
#ColumnInfo(name = "thing2")
val instructions: String,
#ColumnInfo(name = "thing3")
val instructions: String,
#ColumnInfo(name = "thing4")
val instructions: String
) {
#Ignore
var local: String? = null
}
Using the #Ignore annotation, to try and ignore the local string on a generic update. Then provide a custom update statement to just save the local info
#Query("UPDATE foo SET local = :newLocal WHERE foo.id = :id")
fun updateLocal(id: Long, newLocal: String)
However ROOM seems to be smart enough to check that I used #Ignore on the local property and it will not compile with that update statement.
Any ideas?
Partial Updates got added to Room in 2.2.0
In Dao you do the following:
// Here you specify the target entity
#Update(entity = Foo::class)
fun update(partialFoo: PartialFoo)
And along your entity Foo create a PartialFoo containing the primary key and the fields you want to update.
#Entity
class PartialFoo {
#ColumnInfo(name = "id")
val id: Long,
#ColumnInfo(name = "thing1")
val instructions: String,
}
https://stackoverflow.com/a/59834309/1724097
Simple answer is NO. Room doesn't have conditional insertion or partial insertion.
You have to come up with your insertion logic. The best one I guess is call both database and server for data and just update your server response' local value with your database response' local value.
If you are comfortable with Rx, then you can do something like this
localDb.getFoo("id")
.zipWith(
remoteServer.getFoo("id"),
BiFunction<Foo, Foo, Foo> { localFoo, remoteFoo ->
remoteFoo.local = localFoo.local
remoteFoo
}
)
Another possible way is to write custom #Query that you insert all the values except local, but it's not feasible if you have lots of fields.

Categories

Resources