I'm wondering how to inhire to a data class in Room database.
As example a have something like this
#Entity(tablename = "user")
abstract class User {
var name: String,
var id: Int
}
Now I want to create a specific user, that has all the attributes of the User class as an entity to store and load them from database
#Entity(tablename = "user")
data class FreeUser {
override var name: String,
override var id: Int,
var freeUserAttirbute: String
} : User ()
and
#Entity(tablename = "user")
data class PremiumUser {
override var name: String,
override var id: Int,
var premiumUserAttirbute: String
} : User ()
as a DAO I would write something like this
interface UserDao {
#Query("SELECT * FROM 'user'")
fun getAllUser(): List<User>
Now when I build my application room is telling me, that the User class can't be abstract. How can I solve this inheritance issue in data class and Room?
You can try this way
open class User {
open var name: String = ""
open var id: Int = 0
}
data class PremiumUser(
override var name: String,
override var id: Int,
var premiumUserAttirbute: String
) : User()
data class FreeUser(
override var name: String,
override var id: Int,
var freeUserAttirbute: String
) : User ()
You can't have every entity with same table name.
If you retrieve User object from database you won't have additional fields from Premium or Free user.
If this is your reals use case than you should be fine with one entity that has name, id and flag that is boolean and if true than it is premium if is false than is free user.
There is not really much sense in using inheritence with data classes because it cancels out their purpose.
Edit answer to question in comments
Create one entity
#Entity(tablename = "users")
data class User(
var id: Int,
var name: String,
var isPremium: Boolean
)
and DAO should look like this
interface UserDao {
#Query("SELECT * FROM users")
fun getAllUsers(): List<User>
}
This whay you get all users. If you want to see who is premium then just check if field isPremium is true. If user is free user, then isPremium should be false.
Related
This seems like a lazy question, but the offical docs literally don't explain how to do this.
https://developer.android.com/training/data-storage/room
I have an entity like so:
#Entity
data class Shader(
#PrimaryKey(autoGenerate = true) val uid: Int,
val name: String,
val shaderMainText: String,
val paramsJson: String?
)
And some methods on my Dao:
#Insert
fun insertAll(vararg shaders: Shader)
#Delete
fun delete(shader: Shader)
#Update
fun update(shader: Shader)
How do I make a new record and insert it?
I've gotten so far as this:
val record = Shader(name="Foo", shaderMainText="bar", paramsJson=null)
But it complains that I'm missing the argument uid. Why do I need to provide a uid? It's supposed to be auto-generated?
An example of creating a new record would be appreciated.
You can set 0 for the default value of your uid field
#Entity
data class Shader(
#PrimaryKey(autoGenerate = true) val uid: Int = 0,
val name: String,
val shaderMainText: String,
val paramsJson: String?
)
When autoGenerate is true, Room ignores the default value, so it creates auto-incremented values.
Finally, this doesn't give you a compilation error:
val record = Shader(name="Foo", shaderMainText="bar", paramsJson=null)
The docs say:
If the field type is long or int (or its TypeConverter converts it to a long or int), Insert methods treat 0 as not-set while inserting the item.
If the field's type is Integer or Long (or its TypeConverter converts it to an Integer or a Long), Insert methods treat null as not-set while inserting the item.
So I would try setting the uid to be initialized to 0 or make it nullable and set to null.
Hope that helps!
https://developer.android.com/reference/android/arch/persistence/room/PrimaryKey#autoGenerate()
You Can Also try like this
The best practice is you always create an instance Class
1st Create a Database instance class
#Database(entities = [Shader::class], version = 1)
abstract class DatabaseHelper : RoomDatabase() {
companion object {
private var instance: DatabaseHelper? = null
fun getInstance(context: Context?): DatabaseHelper? {
if (instance == null) {
synchronized(DatabaseHelper::class) {
if (context != null) {
instance = Room.databaseBuilder(context.applicationContext, DatabaseHelper::class.java, "database.db").allowMainThreadQueries().build()
}
}
}
return instance
}
}
abstract fun getDao(): DBDao?
}
Create Your Shader data class like below
#Entity(tableName = "shader")
data class Shader(
#PrimaryKey(autoGenerate = true) var id: Int?,
val name: String,
val shaderMainText: String,
val paramsJson: String?
) {
constructor(name: String, shaderMainText: String, paramsJson: String?) : this(
null,
name,
shaderMainText,
paramsJson
)
}
Here you want to create constructor
Add data in Shader and insertAll Like Below
val record = Shader(name="Foo", shaderMainText="bar", paramsJson=null)
DatabaseHelper.getInstance(this)?.getDao()?.insertAll(record)
And Your Data added is successfully
There should be always a #PrimaryKey(autoGenerate = false) while inserting data in the DB
I have list of custom object that i wanna save it in the database.
So I have to use TypeConverters to make this possible.
My problem that i get an error when I implement the functionality and I noticed
that a function annotated with TypeConverter never used
Here's the error:
A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
error: The columns returned by the query does not have the fields [avatar,email,first_name,id,last_name] in com.hraa.fakeusers.model.UserData even though they are annotated as non-null or primitive. Columns returned by the query: [data]
public abstract androidx.lifecycle.LiveData<java.util.List<com.hraa.fakeusers.model.UserData>> getUsers();
And here's the code:
#Entity(tableName = USER_DATA_TABLE)
data class DataModel(
val data: List<UserData>,
val page: Int,
val per_page: Int,
val total: Int,
val total_pages: Int
) {
#PrimaryKey(autoGenerate = true)
var id: Int? = null
}
data class UserData(
val avatar: String,
val email: String,
val first_name: String,
val id: Int,
val last_name: String
)
class Converters {
#TypeConverter
fun toUsersData(value: String): List<UserData> {
val type = object : TypeToken<List<UserData>>() {}.type
return Gson().fromJson(value, type)
}
#TypeConverter
fun fromUsersData(usersData: List<UserData>): String {
return Gson().toJson(usersData)
}
}
#Database(entities = [DataModel::class], version = 1, exportSchema = false)
#TypeConverters(Converters::class)
abstract class AppDatabase: RoomDatabase() {
abstract fun dataDao(): DataDao
}
#Dao
interface DataDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertData(data: DataModel)
#Delete
suspend fun deleteData(data: DataModel)
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserData>>
}
Note: toUsersData() function never used.. I don't know why.
Note: toUsersData() function never used
How can you be sure of that? My guess is that this function could work well, but you have two type's transformations here:
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserData>>
Transformation #1 (row level). Input: String (saved in db). Output: data (List).
That should be processed well thanks to your toUsersData() method (may be not, I've not checked, but it seems it should do)
Transformation #2 (row level). Input: data (List). Output: UserData (According to your desired return type). For that Room doesn't know how to do this transformation, so you have en error.
To check if your toUsersData() really works you can test next query:
#Query("SELECT * FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<DataModel>>
If your build is successful, then there is no problem with this function. You can also find this function in Java-class, that was autogenerated by Room during build.
You can try to add another data class:
data class UserDataList(
val data: List<UserData>
)
and change your data method to:
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserDataList>>
I have a list of items like the below that I would like to enter into a database using room.
data class MyRotasDayItem(
#PrimaryKey
#SerializedName("id")
val id: Long,
#SerializedName("date")
val date: String,
#Embedded
#SerializedName("dayEvents")
val dayEvents: List<SealedObj>
)
However I cant seem to add dayEvents. Even if I made the type List I get...
Entities and POJOs must have a usable public constructor
Do i have to use a type converter?
What if in the list Type is a Sealed class that contain other data objects like...
sealed class MySealedExample(
open val foo: Long,
open val bar: Long
) {
#PrimaryKey(autoGenerate = true)
var id: Int = 0
#Entity
data class AnExample1(
#Ignore override val foo: Long,
#Ignore override val bar: Long,
val something:String
) : MySealedExample(foo, bar)
#Entity
data class AnExample2(
#Ignore override val foo: Long,
#Ignore override val bar: Long,
val somethingElse:List<SomeObj>
) : MySealedExample(foo, bar)
}
Anyway to insert that into the database?
Thankyou
Use type converters, I ran into a similar problem and fixed it using type converters.
To convert sealed classes into string and vice versa, I used Gson extension from this gist.
#JvmStatic
#TypeConverter
fun sealedClassToString(sealedClass: SealedClass) : String = GsonExtension.toJson(sealedClass)
#JvmStatic
#TypeConverter
fun sealedClassFromString(sealedClass: String) : SealedClass = sealedClass.let { GsonExtension.fromJson(it) }
I want to store Strings (uids) in my Room DB. I want these uids to be able to be:
Queried (check to see if a uid exists in my list of strings)
Added to (add a uid to the list of strings)
What is the most efficient way to achieve this?
Here is my skeleton to give you some context, though my understanding of Room is very basic so there will be mistakes:
#Entity(tableName = "user_data")
data class UserData(
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "matched_users") var matchedUsers: Set<String>
)
#Dao
interface UserDataDao {
// Check to see if the uid is in matchedUsers Set/List
#Query("SELECT * FROM user_data WHERE :matchId IN matched_users")
fun matchedBefore(matchId: String): Boolean
// Add uid to Set/List
#Insert(onConflict = OnConflictStrategy.ABORT)
fun addMatchUid(uid: String)
}
Any suggestions appreciated.
According to your context, it seems you want to build one-to-many relationship, to do so you can follow the bellow codes. If not let me know what exactly you want.
#Entity(tableName = "user_data")
data class UserData(
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "name") var name: String
)
#Entity(tableName = "user_matcher")
data class UserMatcher(
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "userId") var userId: Int
)
#Dao
interface UserMatcherDao {
// Check to see if the uid is in matchedUsers Set/List
#Query("SELECT m.id as mid, u.id as uid, u.name FROM user_matcher m INNER JOIN user_data u ON m.userId = u.id WHERE m.id = :matchId")
fun getMatchedUsers(matchId: Int): List<UserData>
}
I have 4 different entities with the same type of data..
class {
val Int
val String
val String
}
I am using ViewModel to request the data and right now I have for Observables which updates the list adapter.
tagsViewModel.getAllText().observe(this,
Observer<List<Text>> { t -> adapter.setTags(t!!) })
My problem is that I am having some troubles when updating the ui so I wanted to just do one request to get the 4 different types of entities but I don't know how to get one only list with all different classes.
This is a class type
#Entity(tableName = "text")
data class Text(override var content: String, override var date: Long, override var type: String = AppConstants.TYPE_TEXT) : BaseTag() {
#PrimaryKey(autoGenerate = true)
override var id: Int = 0
}
and Base interface
abstract class BaseTag {
abstract val content: String?
abstract val date: Long?
abstract val id: Int?
abstract val type: String?
}
I would like to do this request:
{ texts : [Text,Text,Text],
emails : [Email,Email,Email]...
}
So... Is there any simple way to do this?
Thanks for any help.