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.
Related
I am quite familiar with these kind of thing on Spring JPA but I can not make it work on Android. I post my code below so you can understand the question better. I have two classes and a third class that needs to contain objects of class 1 and 2. I know that the code for the third class is not correct, since Room does not support object references like that. If I save just the id-s of the objects as foreign keys I am not able to query after the results.
There must be some kind of nice solution for this problem. Thank you.
#Entity(tableName = "soup")
public class Soup {
#PrimaryKey(autoGenerate = true)
private long id;
private String name;
// getter and setters
}
#Entity(tableName = "main_course")
public class MainCourse {
#PrimaryKey(autoGenerate = true)
private long id;
private String name;
// getter and setters
}
#Entity(tableName = "menu")
public class Menu {
#PrimaryKey(autoGenerate = true)
private long id;
private Soup soupOptionOne;
private Soup soupOptionTwo;
private Soup soupOptionThree;
private MainCourse courseOptionOne;
private MainCourse courseOptionTwo;
private MainCourse courseOptionThree;
}
I've been using SugarDB for most of my projects in the past. It was easy to use and satisfied most of my requirements but since that project has been abandoned, decided to look at alternatives and Room seems like the best option.
However, some basic things are quite confusing in Room. My Object uses Gson to populate data from a webservice, and as such as links to other objects. As an example, consider the classes below:
#Entity
public class TestModel
{
#PrimaryKey(autoGenerate = true)
private int id;
private String name;
private String age;
private List<Book> issuedBooks;
}
public class Book
{
private String title;
private int ISBN;
}
Now if my first class is annotated as the Entity, will this automatically treat classes referenced inside it as entities as well?
If I save an object of TestModel, will it save the list of Books with it to the database?
I guess you can do it this way.
#Entity
public class TestModel {
#PrimaryKey
public int id; // TestModel id
public String name;
public String age;
}
#Entity
public class Book {
#PrimaryKey
public int id; // Book id
public int testModelId; // TestModel id
public String title;
public int ISBN;
}
public class TestModelWithBooks {
#Embedded
public TestModel testModel;
#Relation(parentColumn = "id", entityColumn = "testModelId", entity = Book.class)
public List<Book> books;
}
For their Dao, you can write it this way.
#Dao
public interface TestModelDao {
#Query("SELECT * FROM TestModel")
public List<TestModelWithBooks> loadTestModelsWithBooks();
}
will this automatically treat classes referenced inside it as entities as well?
No. In fact, I would expect your code to fail to compile. You would need to:
Make Book be an #Entity
Remove issuedBooks from TestModel
Set up a #ForeignKey relationship between Book and TestModel
If I save an object of TestModel, will it save the list of Books with it to the database?
No.
Room is not an ORM. Room is a thin object wrapper around SQLite. #Entity and #ForeignKey model the table structure. IMHO, the simplest way to think of Room as it being DTOs to the database. Your model objects that represent your object graph are not the entities, but instead are built from the entities. This is akin to how responses from a Web service (e.g., Retrofit) are DTOs to the server, and you may need to map from those objects to the "real" model objects that you want to use in the app.
In the room documentation there is this example of how to use #Relation to get all 'Users' and all 'Pets'
public class User {
int id;
// other fields
}
public class PetNameAndId {
int id;
String name;
}
public class UserAllPets {
#Embedded
public User user;
#Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class)
public List pets;
}
#Dao
public interface UserPetDao {
#Query("SELECT * from User WHERE age > :minAge")
public List<UserAllPets> loadUserAndPets(int minAge);
}
However, if instead of wanting to get all Pets, in this scenario, I just wanted all Users and most recent Pet created, A group by userid.
Would I just filter the results after querying the database, or would it be better to create a foreign key query.
I am having a hard time getting a list item into room. the list item is called measurements and its of type Measurement. the list item has no primarykey that would be related to the database.
but i have no problem adding the same primary key for the ProductModel if necessary.
Here is what i have so far:
#Entity(tableName = TABLE_NAME)
public class ProductModel {
public static final String TABLE_NAME = "product";
#PrimaryKey
private int idProduct;
private int idCategoryDefault;
#Relation(parentColumn = "idProduct", entityColumn = "idProduct", entity = SortedAttribute.class)
private List<SortedAttribute> sortedAttributes = null;
}
#Entity
public class SortedAttribute {
#PrimaryKey
private int idProduct;
private String reference;
#Embedded
private List<Measurement> measurements = null; //****how do i get this into room ? its a LIST of measurements, not a measurement so calling Embedded i think wont work as it cant flatten it****/
}
public class Measurement {
private String value;
private String valueCm;
public Measurement() {
}
}
Embedded annotation can be used on a POJO or Entity only, not for a List. So, Room can not automatically flatten your list in this case.
You can use TypeConverter to convert List<Measurement> into String(in JSON format) and vise versa. You can use any JSON parser library to support it. For example, I use Gson as following.
public class ProductTypeConverters {
#TypeConverter
public static List<Measurement> stringToMeasurements(String json) {
Gson gson = new Gson();
Type type = new TypeToken<List<Measurement>>() {}.getType();
List<Measurement> measurements = gson.fromJson(json, type);
return measurements;
}
#TypeConverter
public static String measurementsToString(List<Measurement> list) {
Gson gson = new Gson();
Type type = new TypeToken<List<Measurement>>() {}.getType();
String json = gson.toJson(list, type);
return json;
}
}
#Entity
#TypeConverters(ProductTypeConverter.class)
public class SortedAttribute {
#PrimaryKey
private int idProduct;
private String reference;
private List<Measurement> measurements = null;
}
EDIT: Use a type converter
#Relation is what you are looking for.
https://developer.android.com/reference/android/arch/persistence/room/Relation.html
From the Room docs:
#Entity
public class Pet {
# PrimaryKey
int petId;
String name;
}
public class UserNameAndAllPets {
public int userId;
public String name;
#Relation(parentColumn = "petId", entityColumn = "userId")
public List<Pet> pets;
}
#Dao
public interface UserPetDao {
#Query("SELECT petId, name from User")
public List<UserNameAndAllPets> loadUserAndPets();
}
Note: Upon further research, Room does not quite support lists of objects that are INSIDE objects. I (and others) have opted to handle the lists separately. Room can handle lists of objects just fine as long as they aren't within an object; so as long as your items inside the list are related to your overall object you can recover the list.
So, you would actually #Ignore the list and just handle it in your Dao abstract class. I could not find the SO post's I found earlier that portray this.
I am using ActiveAndroid and have two models:
#Table(name = "Topics")
public class Topic extends Model {
#Column(name = "Name") String name;
#SerializedName("id")
#Column(index = true,
unique = true,
onUniqueConflict = Column.ConflictAction.REPLACE) public long topic_id;
#Column(name = "Counts",
onUpdate = Column.ForeignKeyAction.CASCADE,
onDelete = Column.ForeignKeyAction.CASCADE) Counts counts;
}
and
#Table(name = "Counts")
public class Counts extends Model {
#Column(name = "Users") int users;
}
Constructors have been excluded for brevity.
Now, when i save Topic, I expect the Counts to be saved. But it does not.
I am using gson to create the models from json. Any reason why counts are not being loaded?
First you have to call count.save() then you can call topic.save(). ActiveAndroid needs the id to get the reference and that happen when you save it.