Room Database with UNIQUE constraint on multiple columns - android

I'm trying to build a Room database which will apply a unique constraint to a combination of three columns, so a conflict will only emerge if all three columns of the incoming record are matched by all three of an existing record.
My current attempt (below) gives a conflict if any of the three columns conflicts, and I'm finding it difficult to discover a solution to this.
Database class:
#Database(entities = {OutputPreset.class}, version = 1)
public abstract class PresetDatabase extends RoomDatabase {
static final String FILENAME = "presets.db";
public abstract PresetDao getDao();
}
Simplified OutputPreset class:
#Entity(tableName = "Presets", indices = { #Index(value = {"Address", "Port", "Protocol"}, unique = true) } )
public class OutputPreset {
#PrimaryKey
int id;
#ColumnInfo(name = "Protocol")
public final String protocol;
#ColumnInfo(name = "Address")
public final String address;
#ColumnInfo(name = "Port")
public final int port;
...
}
As far as I can tell, the #Index(value = {"Address", "Port", "Protocol"}, unique = true) statement is just applying the uniqueness constraint to each column individually, rather than grouping them.
Any help is much appreciated!
UPDATE
Turns out the issue was that the Primary Key wasn't auto-generating for each record, so they were just overwriting each other whenever I inserted something. Fixed by setting the ID's annotation to #PrimaryKey(autoGenerate = true)

Related

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

Room query with column alias returning null

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.

Greendao 3, make users table with no duplicate user names

I ask this question after searching for hours for the solution to this problem and did not find it.
I have build a database with three columns: username, password and age.
I am able to isert new users and update them, but I want it to be impossible to insert two identical user names. I know how to do it in sqlite but in greendao it just does not go for me. thank you.
here is my UsersClass:
#Entity(nameInDb = "users")
public class Users{
#Id(autoincrement = true)
private Long id;
#NotNull
private String userName;
#NotNull
private String password;
#NotNull
private int age;
}
The #Unique annotation should work for you.
#Entity(nameInDb = "users")
public class Users{
#Id(autoincrement = true)
private Long id;
#Unique
private String userName;
#NotNull
private String password;
#NotNull
private int age;
}
If you need to customize further you can add options to the #Entity annotation
#Entity(
...
// Define indexes spanning multiple columns here.
indexes = {
#Index(value = "name DESC", unique = true)
},
...
)

Not able to get query results using Room persistence

I am developing an Android app using the Room persistence library. I have a User and a Car entity
#Entity(tableName = "users")
public class User {
#PrimaryKey
#NonNull
private int id;
private String name;
public User(#NonNull int id, String name) {
this.id = id;
this.name = name;
}
}
and
#Entity(tableName = "cars", foreignKeys = #ForeignKey(parentColumns =
"id", childColumns = "userId", entity = User.class))
public class Car {
#PrimaryKey(autoGenerate = true)
private int id;
private int userId;
private String brand;
public Car(int userId, String brand) {
this.userId = userId;
this.brand = brand;
}
}
Also I have created a UserWithCar class as below:
public class UserWithCar {
#Embedded(prefix = "user_")
public User user;
#Embedded(prefix = "car_")
public Car car;
}
As you can see in the UserWithCar I use a prefix cause if I don't I get the following error:
Multiple fields have the same columnName: id. Field names: user > id,
car > id.
I want to get all the UserWithCar using the following query:
#Query("SELECT * FROM users JOIN cars ON users.id = cars.userId")
List<UserWithCar> getUserWithCar();
Using this query I get the following error:
The query returns some columns [id, name, id, userId, brand] which are
not use by com.roomdemo.data.models.UserWithCar. You can use
#ColumnInfo annotation on the fields to specify the mapping.
com.roomdemo.data.models.UserWithCar has some fields [user_id,
user_name, car_id, car_userId, car_brand] which are not returned by
the query. If they are not supposed to be read from the result, you
can mark them with #Ignore annotation. You can suppress this warning
by annotating the method with
#SuppressWarnings(RoomWarnings.CURSOR_MISMATCH). Columns returned by
the query: id, name, id, userId, brand. Fields in
com.foodtec.roomdemo.data.models.UserWithCar: user_id, user_name,
car_id, car_userId, car_brand.
Can I have some help? Thanks!
Update
Using #Wizard help, I removed the prefix from the #Embeded and I added #ColumnInfo "uId" for the User's id and "cId for the Car's id in order to not have the same id field. By this way it works!
Columns returned by the query: id, name, id, userId, brand. Fields in
com.foodtec.roomdemo.data.models.UserWithCar: user_id, user_name,
car_id, car_userId, car_brand.
Error indicates that, columns returned by query is different from Pojo class. It should be the same. Alternatively you can map your Pojo variable to column name using #ColumnInfo annotation.
For example,
#PrimaryKey
#NonNull
#ColumnInfo(name = "user_id")
private int id;
This way, id will be mapped to user_id.
Change your query:
#Query("SELECT * FROM users JOIN cars ON users.id = cars.userId")
to specify the columns in the POJO class UserWithCar. The columns returned must match all the columns, and have the same column name as your POJO. You can use AS to change the column names in the query.
#Query("SELECT userId as userId , brand as brand FROM users JOIN cars ON users.id = cars.userId")
Alternatively, you can use #ColumnInfo to specify the column name mappings.

How to handle relationships in Room?

I'm testing Room persistence library. I have two entity classes:
#Entity
public class User {
#PrimaryKey
int id;
String name;
}
#Entity(foreignKeys = #ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "userId")
public class Pet {
#PrimaryKey
int id;
String name;
int userId;
}
Now I'd like to get list of Pet and in each Pet object keep actual reference to User according to his userId. So each time when the userId is changed, this reference also should be changed. Is it possible to do? Or maybe there is a better way to handle relation like this?
Actually, ROOM not recommended to do that. The reasons are in the reference link.
Maybe you could try it,
#Entity
public class User {
#PrimaryKey
int id;
String name;
#Embedded
public List<Pet> userPet;
}
public class Pet {
int id;
String name;
}
Understand why Room doesn't allow object references
A convenience annotation which can be used in a Pojo to automatically fetch relation entities.

Categories

Resources