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.
Related
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)
I am implementing Room Database. Here is my POJO Class
public class Task implements Serializable {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "task_name")
private String task;
#ColumnInfo(name = "description")
private String desc;
#ColumnInfo(name = "finish_by")
private String finishBy;
#ColumnInfo(name = "finished")
private boolean finished;
#ColumnInfo(name="no_of_days")
private String no_of_days;
public String getNo_of_days() {
return no_of_days;
}
public void setNo_of_days(String no_of_days) {
this.no_of_days = no_of_days;
}
}
This is the DAO Class
#Dao
public interface TaskDao {
#Update
void update(Task task);
#Query("SELECT task_name,description,no_of_days FROM task")
List<Task> getTasksandDescription();
}
While running my code, I am getting the following error
com.example.myapplication1.model.Task has some fields [id, finish_by, finished] 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: task_name, description, no_of_days. Fields in com.example.myapplication1.model.Task: id, task_name, description, finish_by, finished, no_of_days.
This is not an error but a warning, it's telling you that you are mapping an SQL to a Pojo but you are not returning all the fields required to set up the Pojo. So your class has more fields than is returned from the query, you can fix this by doing the following.
I. Do a select * to return all the fields
#Query("SELECT * FROM task")
II. Add #Ignore annotation to the fields you're not interested in
III. Create another Java class that contains only the fields you're interested in and return in from the query instead.
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
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.
I new to Room, and #Relation isn't clear for me.
If I understand correctly, I have entities e.g. (RSS)ChannelEntity, and channel has items named ItemEntity.
These are classes wiht #Entity annotation.
I also have a POJO to "connect" my entites. I mean I've to write a POJO like this:
public class Channel {
#Embedded
private ChannelEntity channel;
// Link is the primary key in ChannelyEntity
#Relation (parentColumn = "link", entityColumn = "channel_link")
private ArrayList<ItemEntity> items;
// Getters and Setters are here
}
Than I've to write a dao interface where I can get Channel (not ChannelEntity) like this:
public interface ChannelDao {
#Query("SELECT * FROM channels WHERE link = :link LIMIT 1")
Channel getChannelById(String link);
#Query("SELECT * FROM channels")
ArrayList<Channel> getAllChannels();
}
With these Entities, DAOs and POJOs I can get Channel objects which contain a list of Items with the corresponding link (id). Is that right?
My other is question about the rest CRUD. E.g. if I'd like to save a new channel can I add this statement to my ChannelDao?
#Insert(onConflict = OnConflictStrategy.REPLACE)
void createChannels(Channel... channels);
to delete
#Delete
void deleteChannels(Channel... channels);
and so on. So will it create and delete the ChannelEntities and ItemEntities from the passed Channel object?
I updated my classes as #CommonsWare adviced it.
Now I have entity classes with #Embedded object:
#Entity (tableName = "channels")
public class ChannelEntity {
// Required channel elements
// The name of the channel. It's how people refer to your service.
private String title;
// The URL of the HTML website corresponding to the channel
#PrimaryKey
private String link;
//other fileds
#Embedded
private TextInputEntity textInputEntity;
#Embedded
private ImageEntity imageEntity;
//getters and setters
}
One of the embedded classes:
// Specifies a text input box displayed with the channel.
// Embedded in ChannelEntity
public class TextInputEntity {
// Required elements
// The label of the Submit button in the text input area.
private String title;
// Explains the text input aera.
private String description;
// The name of the text object int hte text input area.
private String name;
// The URL of the CGI script that processes the text input request
private String link;
#ColumnInfo (name = "channel_link")
private String channelLink;
}
I've wrote daos for all classes have #Entity annotation(I called embedded classes entity even they aren't, but I'd have model classes for views and I don't want to mix up them later).
I mapped relations like this:
// All items are optional, but at least one of title or description must be presented.
#Entity (tableName = "items"
, foreignKeys = #ForeignKey (entity = Channel.class
, parentColumns = "link"
, childColumns = "channel_link"))
public class ItemEntity {
#PrimaryKey (autoGenerate = true)
private int id;
// Title of the item
private String title;
// Other fileds
#ColumnInfo (name = "channel_link")
private String channelLink;
// Getters and setters
And when I want to get a Channel with the list of items I get it like this:
public class Channel {
#Embedded
private ChannelEntity channel;
#Relation (parentColumn = "link", entityColumn = "channel_link")
private ArrayList<ItemEntity> items;
#Relation (parentColumn = "link", entityColumn = "channel_link")
private ArrayList<SkipDayEntity> skipDays;
#Relation (parentColumn = "link", entityColumn = "channel_link")
private ArrayList<SkipHourEntity> skipHours;
//Setters and getters
}
And this is the dao for Channel:
#Dao
public interface ChannelDao {
#Insert (onConflict = OnConflictStrategy.REPLACE)
void insertChannel(ChannelEntity channel);
#Update
void updateChannel(ChannelEntity channel);
#Delete
void deleteChannel(ChannelEntity channel);
#Query ("SELECT * FROM channles WHERE link = :link LIMIT 1")
Channel getChannelByLink(String link);
#Query ("SELECT * FROM channels")
LiveData<ArrayList<Channel>> getAllChannels();
}