#Entity(tableName="user_table")
data class User(
#PriamryKey(autoGenerate = false) val id:Int,
#Embeded(prefix="address_") val address:Address
)
#Entity(tableName = "address_table")
data class Address(
#PrimaryKey(autoGenerate = false) val id:Int
)
Is there a way to just ignore the Id column from Address table because from my knowledge I will be getting
columns id, address_id in the user object once created
I have similar columns here and there and some are no longer in use once I create views for these tables i.e foreign keys etc
Is there a way to just ignore the Id column from Address.
I don't believe so. Assuming that Address has other fields (otherwise simply don't Embed) then to Embed an Entity with an Entity is de-normalising the database.
If you have decided that you don't need to store the addresses in a seperate table as there is effectively a 1-1 relationship then you should do away with the address table, which could/would be converting the Address class to not be an Entity and remove the id field.
If you were to keep the Address class as an Entity then you cannot #Ignore the #PrimaryKey as Room requires that an Entity has an #PrimaryKey. Really, in this situation of having another table for the address, you should reference/map the address to not de-normalise and thus just have a field in the user_table for that reference/mapping rather than Embed. You would/could then have a non-entity class (POJO) for extracting data that would use Embed both Entities or Embed one and Relate the other.
Related
I've found literally 0 articles/threads about this error on the internet so I'm absolutely clueless, so here it goes.
I have a class A and a class B.
class A is having an ArrayList of class B instances like this:
#Entity
class A {
#PrimaryKey
var id = 0
var listOfB: ArrayList<B>
}
And here it goes the B class:
#Entity
class B {
#PrimaryKey
var id = 0
var specificDate: Date? = null
}
Whenever I try to run the project I get the following error message:
Cannot use unbound fields in entities.
Which points exactly to the ArrayList of B instances inside the A class.
What may be causing this?
An Entity has two purposes:
It defines the columns of a table in the database.
An instance of the entity represents one row of an element in that database, which can be given to you as a query result, or you can pass to Room to add or edit a row in the corresponding table.
SQL/SQLite doesn't have the concept of a table column where the type is another table or list of rows from another table, so what you're defining doesn't make sense. (Incidentally, you cannot use Lists of any type for a column type in a table. You can only use primitive type classes in an Entity class since that's all that SQL supports.)
Instead, you must define a relationship between your two tables. In this case, you can remove the list property from your class A. I'm not sure if you're trying to define a one-to-many or many-to-many relationship, but you can look at the Room documentation here for an example of how to set up relationships.
Basically, if a B can only be a member of a single A at a time, B should reference an A row ID in one of its columns, and you have a one-to-many relationship. Room will let you create a third data class (not an entity) to define the relationship in a natural way that looks similar to your example class A and can be returned in queries.
If B can belong to multiple different A's, then you have a many-to-many relationship, which is more complicated. A third table (Entity class) has to be defined to link the relationships together.
That is my current dao
#PrimaryKey(autoGenerate = true)
val id: Int,
val name: String,
val date: LocalDate,
val amount: Int,
val uri: String,
val tag: String,
val toList: Boolean,
val inUse: Boolean,
val listValue: Int
now I have the problem that in a previous version of that dao there is a variable in that table that I now want to remove.
I found a 4 step guid:
1.) create new table 2.) insert from the old table 3.) drop the old table 4.) alter new table name back to old table name
that's fine but my problem is that I have a variable with a LocalDate which uses a DateTypeConverter to function properly.
How do I insert that LocalDate into the new table? I just know of TEXT and INTEGER
Step 2 use the SupportSQliteDatabase's execSQL method to execeute a query based upon the SQL
INSERT INTO <the_table> SELECT <the_columns> FROM <the_old_table>;
Where:-
anything enclosed within <> needs to be altered accordingly as per:-
<the_table> should be replaced with the new table name.
<the_columns> should be replaced with the column names, separated by commas, LESS THE DROPPED COLUMN NAME
<the_old_table> should be replaced with the old/original table name.
Note that a variable name will be the same as the variable name.
The above will copy the values, whatever they are, as stored in the database, from the old to the new table.
The TypeConverters are only used to convert the data to or from the respective object (LocalDate in your case) when storing or retrieving the stored data.
A type converter should consist of two functions:-
1 to convert the object to a type that can be stored in an SQLite database (SQLite is a universal database that has no concept of a programming languages objects). The SQLite types being
INTEGER (not necessarily a Kotlin Int, could be a Long, Byte even a Boolean ....).
TEXT (a Kotlin String ....)
REAL (Kolin Double, Float ....)
BLOB (Kotlin ByteArray ....)
NULL
2 to convert the stored type into the object when retrieving data from the database. As such it is no issue at all for the INSERT INTO table SELECT ....; to copy the existing data from one table to another irrespective of Room's handling of the data when it stores and retrieves the data.
The result being that the data is stored in the database as either one of the 5 types. As such it is no is
If the "current dao" (it is not a dao, it is an entity that should be annotated with #Entity, which equates to a table) is after the removal of the dropped variable then you would use:-
INSERT INTO <the_new_table> SELECT id,name,date,amount,uri,tag,toList,inUse,listValue FROM <the_old_table>;
You may wish to refer to 2. INSERT INTO table SELECT ...;
If you want to add data into date column of old table data.
fun updateData() {
val list = dao.getAllData()
list.forEach {
//update data
}
dao.saveData(list)
}
While searching for this, I only came across people asking how to Avoid inserting duplicate rows using room db. But my app has a feature where the user may tap a copy button and the list item will get inserted again in the db. I could have simply achieved this if my table didn't have a primary key set on one of its fields. While I found this solution for SQLite, I don't know how I can achieve this in Room Db. Because while writing an insert query with custom queries in room would defeat the purpose of using room in the first place.
Let's say you have some entity
#Entity(tableName = "foo_table")
data class Foo (
#PrimaryKey(autoGenerate = true) var id: Int,
// or without autogeneration
// #PrimaryKey var id: Int = 0,
var bar:String
)
and you have some Dao with insert:
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(foo: Foo)
Then to copy your existing value (copiedValue: Foo) you need in some way to manage your primary key:
Scenario 1. Your Primary Key is autogenerated, you have to set it to default value to get new autogenerated one:
copiedValue.id = 0
yourDao.insert(copiedValue)
Scenario 2. Your Primary Key is not autogenerated, you have to set new primary key manually:
copiedValue.id = ... // some code to set new unique id
yourDao.insert(copiedValue)
I have three tables Notes, Tags and join table with foreign keys of Note and Tag called NoteTagJoin, but how can I return Note with all Tags as one response?
Here is query to get Note and Tags:
SELECT n.*,t.* FROM notes n INNER JOIN note_tag_join nt ON n.entryId = nt.noteId INNER JOIN tags t ON t.tagId = nt.tagId WHERE n.entryId =:noteId
And here is response class which has to hold note and list of tags:
data class NoteResponse(
#Embedded
var note: Note? = null,
#Relation(parentColumn = "entryId", entityColumn = "tagId", entity = Tag::class)
var tags: List<Tag>? = null
)
But tags list is empty on response just note is there, I know for sure that Tags and Note exists in db and Join table has right foreign keys because all other queries works so my guess is that Query is wrong or NoteResponse class is wrong because I use annotation #Relation which I don't need, but if I don't add #Relation annotation on tags it throws error that room doesn't know what is this list so how to do it? I can't find any references for this, documentation only mentions embedding one class in POJO but no examples for Lists and all similar posts talks only about inserting list.
please see my realization of many-to-many relationship CinemaActorJoinDao
You can instead my code , replace with your , if you have any question , i will try ask to you :)
While playing with the Room Persistence Library I came to know that there is no methodology to set a data class field with NOT NULL and also UNIQUE constraints. whether SQLite supports those constraints. Isn't it a problem to migrate old database where those constraints are used? Can anyone give a suggestion for this issue?
I came to know that there is no methodology to set a data class field with NOT NULL and also UNIQUE constraints
A #NonNull annotation on an #Entity field will cause that field's column to have NOT NULL applied to it.
unique=true on an #Index will enforce a uniqueness constraint (e.g., #Entity(indices={#Index(value="something", unique=true)}). However, you are correct that a plain UNIQUE constraint on a column, other than via an index, is not supported.
Isn't it a problem to migrate old database where those constraints are used?
Room is not designed to support existing database structures, particularly in the now-current alpha state. Over time, I would expect Room to support a higher percentage of SQLite features, though I will be stunned if it ever reaches 100%.
Complementary answer about NOT NULL for those using Kotlin:
please note that marking a type as non optional will automatically make it not null (and an optional type will not do that).
You can check it in the schema generated by room with #Database(exportSchema = true) on your database.
For example I have something like that:
#Entity(tableName = "messages")
data class Message (
#PrimaryKey
val messageId: UUID = UUID.randomUUID(),
val date: Date = Date(),
val receivedDate: Date? = null
)
And in the generated schema I can read:
"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageId` TEXT NOT NULL, `date` INTEGER NOT NULL, `receivedDate` INTEGER, PRIMARY KEY(`messageId`))"
(Note: the Date type is here an Int and the UUID a string due to converters I use elsewhere)
If you have multiple item that is to be marked unique & based on that you want to insert in db then you can use composite primary key.
For Not null, Room has provided "#NonNull" annotation that is added over the field that cannot be null.
In the below mentioned eg. roll number is unique in each class but 2 different class can have same roll number. So we can use class & rollNumber as composite primary key & insert in db uniquely.
Example:
#Entity(primaryKeys = {"rollNumber", "class"})
class Student {
#NonNull
private int rollNumber;
private String firstName;
private String lastName;
private int class;
}
for a null able field you can use wrapper primitive type java. for example use Integer instance int in your Room Table.
as in wrapper primitive type java this can bee null but primitive type class cant bee null. and in generation of this SQL code for primitive field that use notNull=true but when use Integer in generayion SQL code use notNull=false