Room query with column alias returning null - android

I've been struggling with this and I haven't found a solution.
Let's say I have this sample entity:
#Entity (tableName="test")
public class Test{
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
private long id;
#ColumnInfo(name = "field1")
#NonNull
private double field1;
#Ignore
#ColumnInfo(name = "queryField")
private transient String queryField;
}
So basically my table has id and field1 while queryField is not a column table but it's used for instance on screens to show extra details from another table.
Then in my DAO
...
#Query("Select a.id, a.field1, b.column as queryField
"from test a inner join some_table on..." +
"where a.id=:someId" +
"order by i.name ASC")
LiveData<List<Test>> get(long someId);
....
So running someting like this, and the query result while it contains test.id and test.field1 info it does not fill the queryField property which remains null.
#Entity does not allow this kind of queries? Do I need to generate another class to add the new column? I'd like to mix Entities and Pojos in the project to keep things simple.

Related

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.

Migration in Room with an Android Array of Strings

I have a database in Android with Room from which I have deleted a column. I was doing the migration, and I saw that it was not as simple as doing a DROP of the deleted column.
Then I have seen that I have to take a series of steps, creating a provisional table that will later be the new table with the deleted column, but the problem is that this table contains a field that is a String Array that I don't know how to declare in SQL.
#Entity(tableName = "recipe_table")
data class RecipesDb(
#PrimaryKey
#ColumnInfo(name = "id")
val id: Long,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "category")
val category: List<String>,
#ColumnInfo(name = "isRecommended")
val isRecommended: Boolean,
#ColumnInfo(name = "images")
val images: List<String>,
#ColumnInfo(name = "ingredients")
val ingredients: List<String>,
#ColumnInfo(name = "date")
val date: Long,
#ColumnInfo(name = "time")
val time: Int,
#ColumnInfo(name = "difficult")
val difficult: String,
#ColumnInfo(name = "originalUrl")
val originalURL: String? = null,
#ColumnInfo(name = "author")
val author: String,
#ColumnInfo(name = "siteName")
val siteName: String
)
And now I have removed the ingredients column. I wanted to do something like this:
private val MIGRATION_3_2 = object : Migration(3,2) {
override fun migrate(database: SupportSQLiteDatabase) {
//Drop column isn't supported by SQLite, so the data must manually be moved
with(database) {
execSQL("CREATE TABLE Users_Backup (id INTEGER, name TEXT, PRIMARY KEY (id))")
execSQL("INSERT INTO Users_Backup SELECT id, name FROM Users")
execSQL("DROP TABLE Users")
execSQL("ALTER TABLE Users_Backup RENAME to Users")
}
}
}
But when I declare the new temporary table User_Backup, I have no idea how to specify that one of the fields is an Array. In the end I was able to do it with Room's AutoMigrations and creating an interface, but I would like to know how to do it this way as well.
The simple way is to compile the code (Ctrl+F9) with the changed #Entity annotated classes in the list of entities of the #Database annotation.
Then look at the generated java (visible via the Android View in Android Studio). There will be a class that is the same name as the #Database annotated class but suffixed with _Impl.
In this class there will be a method that is named createAllTables, This includes the SQL that room uses for creating the tables.
Just copy and paste the appropriate SQL and then change the table name, this will not only use the correct type but also apply the correct column constraints that Room expects.
I would suggest
Adding an execSQL("DROP TABLE IF EXISTS the_backup_table_name;") before you create a table (just in case it already exists)
And instead of using execSQL("DROP TABLE Users") to use execSQL("DROP TABLE IF EXISTS the_original_table_name")
Personally I always RENAME the table name of the original, then RENAME the new table and then finally DROP the renamed original.
I would use:-
private val MIGRATION_3_2 = object : Migration(3,2) {
override fun migrate(database: SupportSQLiteDatabase) {
//Drop column isn't supported by SQLite, so the data must manually be moved
with(database) {
execSQL("DROP TABLE IF EXISTS Users_Backup")
execSQL("CREATE TABLE IF NOT EXISTS ....) //<<<<< SEE NOTES BELOW, the SQL MUST BE CHANGED.
execSQL("INSERT INTO Users_Backup SELECT id, name FROM Users")
execSQL("ALTER TABLE Users RENAME TO Old_Users")
execSQL("ALTER TABLE Users_Backup RENAME to Users")
execSQL("DROP TABLE IF EXISTS Old_users")
}
}
}
note .... indicates that the SQL is copied from the generated java and that the table name is changed from Users to Users_Backup
The first line will drop the Uers_backup just in case it happens to exist, it's just a little less likely to fail under unusual circumstances.
Rather than dropping the Users table before the RENAME of the Users_Backup to Users. The 4th execSQL changes the name of the Users table, so should there be an issue with changing the Users_Backup table to be the Users table, then the original Uers table is available as Old_users.
When all has been complted then the original Users table, now named Old_Users is then dropped.
These are all just a little safer/secure.

Room database: What is Index specific columns (indices and #Index) and how to use it?

I was referring Index specific columns of Room Database.
Below is some example code is written here https://developer.android.com/training/data-storage/room/defining-data#column-indexing
Example Code-1:
#Entity(indices = {#Index("name"),
#Index(value = {"last_name", "address"})})
public class User {
#PrimaryKey
public int id;
public String firstName;
public String address;
#ColumnInfo(name = "last_name")
public String lastName;
#Ignore
Bitmap picture;
}
Example Code-2:
#Entity(indices = {#Index(value = {"first_name", "last_name"},
unique = true)})
public class User {
#PrimaryKey
public int id;
#ColumnInfo(name = "first_name")
public String firstName;
#ColumnInfo(name = "last_name")
public String lastName;
#Ignore
Bitmap picture;
}
This is described in room documentation by android,
I have used indices for the uniqueness of column but what does above code means can anyone explain it?
Q1: What is the use of indices and #Index?
Q2: What is the difference between #Index("name") and #Index(value = {"last_name", "address"})?
Bit late on the answer. but hope so it will help someone.
Q1: What is the use of indices and #Index?
Indices: Can contain the list of indices on the table. whereas #Index is used to define an index.
For example:
#Entity(indices = {
#Index("FirstIndex"),#Index(value="last_name",unique = true),
#Index("SecondIndex"),#Index(value="first_name", unique = true)
})
As you can see, i have defined two indexes in this example which are separated by coma. Here "FirstIndex" and "SecondIndex" are the name of indexes. If i don't define the name of indexes (as you have quote in your example) then Room will set it to the list of columns joined by '' and prefixed by "index${tableName}". So if you have a table with name "Foo" and with an index of {"bar", "baz"}, generated index name will be "index_Foo_bar_baz".
Q2: What is the difference between #Index("name") and #Index(value = {"last_name", "address"})?
#index("name") //syntax to give name of index.
#index(value=last_name", "address") //Syntax to define, which column(s) need to be indexed
Indexing is the process of adding indexes which are used to quickly locate data without having to search every row in a database table every time the database table is queried or accessed.
Room supports indexing certain fields or indexing group of fields using indices property to speed up the queries. you can check the below link for further knowledge.
https://proandroiddev.com/exploring-room-architecture-component-the-extras-cf3f0259ceed
In Kotlin actually the syntax is a bit different:
#Entity(indices = [Index(value = ["last_name", "address"])])
data class User(
#PrimaryKey val id: Int,
val firstName: String?,
val address: String?,
#ColumnInfo(name = "last_name") val lastName: String?,
#Ignore val picture: Bitmap?
)
This is the Room Reference document with this and lots of other info: RoomDb Reference

Android Room Allow Dao #Query to populate #Ignore columns

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>
}

Insert into multiple tables using Room Persistence Library

It's the first time I am using Room. I have a class called:
#Entity(tableName = "users")
class User{
#PrimaryKey
#ColumnInfo(name = "id")
#SerializedName("id")
String id;
#ColumnInfo(name = "name")
#SerializedName("name")
String name;
#SerializedName("shift")
#Ignore
List<Shift> shifts;
}
#Entity(tableName = "shifts")
class Shift{
#PrimaryKey
#ColumnInfo(name = "id")
#SerializedName("id")
String id;
#ColumnInfo(name = "start_time")
#SerializedName("start_time")
String startTime;
#ColumnInfo(name = "end_time")
#SerializedName("end_time")
String endTime;
}
I want these two to be seperate tables in the database, hence I cannot user #Embedded annotation, as it will create a single table using all field as columns. I am using the above User class to store json reponse from the server as well, where I get the user and shift details information in a json object.
Is there any way I can insert the Shift details in shifts table as soon as I insert the User details in the users table ? I initially thought this will be handled using #Embeded but that will create shift table columns in the user table which I do not want.
Can someone help me as to how I am suppose to handle this in Room Persistence Library. Similar I will have to do for delete as well.
Thanks
Is there any way I can insert the Shift details in shifts table as soon as I insert the User details in the users table ?
Create your own DAO #Transaction method that calls inserts for User and Shift.
Similar I will have to do for delete as well.
If you fix your Shift class, such that it has a #ForeignKey relationship to User with the appropriate cascade-delete option, deleting a User will delete its Shift rows as well. In your current implementation, User and Shift are unrelated.

Categories

Resources