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.
Related
I'm working on dictionnary application in which the api request have many nested lists , i have tried to insert all the nested lists but i'm getting different error each time , i would like to know what is the best way to save multiple nested lists , should i use room relations or something else , thank you in advance for help , i m really stuck with this for few days now
This is sample schema of how are the lists nested
This is the parent list
#Entity(tableName = "DICTIONNARYTABLE")
#TypeConverters(DictionnaryModelConverter::class)
class DictionnaryModel : ArrayList<DictionnaryModelItem>() {
#PrimaryKey(autoGenerate = true)
#NotNull
val wordId: Long = 0
}
The parent list has two lists as well
#Entity
data class DictionnaryModelItem(
#PrimaryKey val dictionnaryModelId: Long = 0,
#TypeConverters(DictionnaryMeaningConverter::class)
val meanings: MutableList<Meaning>,
#TypeConverters(DictionnaryPhoneticsConverter::class)
val phonetics: MutableList<Phonetic>,
val word: String
)
//---------------------------
#Entity
data class Meaning(
#PrimaryKey val meaningId: Long = 0,
#TypeConverters(DictionnaryDefinitionConverter::class)
val definitions: List<Definition>,
val partOfSpeech: String
)
///-------------------------------
#Entity
data class Phonetic(
#PrimaryKey val phoneticId: Long = 0,
val audio: String,
val text: String
)
inside the meaning , i also have definition which another model
#Entity
data class Definition(
#PrimaryKey val definitionId: Long = 0,
val definition: String,
val example: String,
#TypeConverters(DictionnarySynonymsConverter::class)
val synonyms: List<String>
)
You need to create one-to-many relationship data model here. For instance each dictionary word has many meanings and many phonetics. Here Dictionary is a parent entity and Meaning and Phonetic are the child entities. Each child entity will have it's parent entity primary key stored in its table. You will need another data class to define this relationship.
data class DictionaryWithMeanings(
#Embedded val dictionary: Dictionary,
#Relation(
parentColumn = "dictionaryModelId",
entityColumn = "dictionaryId"
)
val meanings: List<Meaning>
)
Meaning table has to store dictionaryId as foreign key its table. Same has to be defined for phonetics. And Meaning table again has similar relationship with Definition and so on.
I have simple data object that I want to insert into room database but as I am using auto increment on my primary key I am doing it as below
#Dao
interface T1Dao {
#Query("INSERT INTO tbl_t1(data1, data2) VALUES ( :T1.data1, :T1.data2) ")
fun insert(note: T1): Long
}
I have many properties in T1 so I don't want pass them separately if possible.
In above example I am just showing two properties.
But you can just use #Insert and not to set your primary key field, can't you?
#Insert
fun insert(note: T1): Long
Let's say you have T1 class:
#Entity
data class T1(
#PrimaryKey(autoGenerate = true)
val id: Int = 0, // This lets you not to set id before inserting
val data1: String,
val data2: String
)
Then you can insert:
dao.insert(T1(data1 = "data1", data2 = "data2")) // just don't set id
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.
When saving a list of objects in my room database using a Dao
#Insert()
fun saveCharmRankMaterialCosts(materialCosts: List<CharmRankCraftingCost>) : List<Long>
And this is used from my repository class to save results from an API call:
val charmRankCosts = CharmRankCraftingCost.fromJsonCraftingCost(
charmRankId.toInt(),
jsonCharmRank.crafting
)
// save crafting/upgrade costs for the rank
val results = charmDao.saveCharmRankMaterialCosts(charmRankCosts)
Log.d("CharmRepository", "Saved charm material costs: ${results.toString()}");
assert(!results.contains(-1))
When running this code, insert ID's are returned and the assertion is never triggered (i.e. no inserts fail).
But when I inspect the data base on the device, most of the supposedly inserted IDs are missing from the table. I'm very confused as to what is going on here. I've debugged this issue for many hours and have been unsuccessful in getting this to work. Is there something obvious I'm missing?
The issue seems to have been related to foreign key constraints. I had a CharmRank data class with multiple related data objects. See below:
/**
* Copyright Paul, 2020
* Part of the MHW Database project.
*
* Licensed under the MIT License
*/
#Entity(tableName = "charm_ranks")
data class CharmRank(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "charm_rank_id")
var id: Int = 0,
#ColumnInfo(name = "charm_id")
var charmId : Int,
#ColumnInfo(name = "charm_rank_level")
var level: Int = 0, // 3
#ColumnInfo(name = "charm_rank_rarity")
var rarity: Int = 0, // 6
#ColumnInfo(name = "charm_rank_name")
var name: String = "",
#ColumnInfo(name = "craftable")
var craftable: Boolean
)
Each charm rank has associated skills and items to craft said rank. These objects are simply relational objects in that they hold the ID of the CharmRank and a SkillRank in the case of the skills object, or the ID of the CharmRank and the ID of the Item object.
data class CharmRankSkill(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "charm_rank_skill_id")
var id: Int,
var charmRankId : Int,
var skillRankId: Int
)
data class CharmRankCraftingCost(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "charm_rank_crafting_cost_id")
var id: Int,
#ColumnInfo(name = "charm_rank_id")
var charmRankId: Int,
#ColumnInfo(name = "charm_rank_crafting_cost_item_quantity")
val quantity: Int,
val itemId: Int
)
Originally in CharmRankCraftingCost, I had a foreign key constraint on the Item object and the CharmRank object. Below is the foreign key constraint on the Item object:
ForeignKey(
entity = Item::class,
parentColumns = ["item_id"],
childColumns = ["itemId"],
onDelete = ForeignKey.CASCADE
)
The Item data object has IDs provided by the remote data source, so when I insert items into it's respective table, the conflict resolution is set to Replace. During the process of saving the relational items to the data base for the CharmRanks, I also have to save the Item objects prior to saving CharmRankCraftingCosts. It seems that what was happening is that when the Item objects are inserted, sometimes the items would get replaced, which would trigger the cascade action of the foreign key resulting in the CharmRankCraftingCosts items I just saved for the CharmRank to be deleted due to the cascading effect.
Removing the foreign key constraint on the Item table solved my issue.
As I understood from the comments, you make a delete before inserts. The problem is that it happens that the insert gets completed before the delete since you do them in separate threads. What you need is to do both in one transaction. Create a method in the the DAO class with #Transaction annotation (Make sure your dao is an abstract class so you can implement the body of this method):
#Dao
public abstract class YourDao{
#Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract List<Long> insertData(List<Data> list);
#Query("DELETE FROM your_table")
public abstract void deleteData();
#Transaction
public void insertAndDeleteInTransaction(List<Data> list) {
// Anything inside this method runs in a single transaction.
deleteData();
insertData(list);
}
}
Read this for Kotlin Version of the code.
I'm wanting my Dao to populate #Ignore columns in my Entity class. For example:
Entity
#Entity(tableName = "example")
data class Example(
#PrimaryKey
val id: Long,
val value: Int
) {
#Ignore
var nextId: Long = 0L
}
Dao
#Dao
interface ExampleDao {
#Query(value = "SELECT *, (id + 1) AS nextId FROM example")
fun getAllExamples(): List<Example>
}
However, when the application gets built, the following warning gets produced:
The query returns some columns [nextId] which are not used by com.example.app.Example and it doesn't populate nextId.
Is it possible to include #Ignore columns in a #Query (if so, how)? If not, what are some strategies that can be employed to populate columns that are not present in my tables into my Entity class.
Note: I'm fully aware with the example provided that I can simply do something like:
#Ignore
val nextId: Long = id + 1
But is not the point of the question I am asking.
Based on the information that #CommonsWare has given me, the solution that I went with is
data class ExampleWithNextId(
#Embedded
val example: Example) {
var nextId: Long = 0L
}
Then use it in Dao like so
#Dao
interface ExampleDao {
#Query(value = "SELECT *, (id + 1) AS nextId FROM example")
fun getAllExamplesWithNextId(): List<ExampleWithNextId>
}