Let's say I have a Patient entity, storing the patient ID, a boolean and finally a Person object. So I annotate these fields with #ColumnInfo to store in the database.
Now a Person has 2 String fields: a first name and last name.
However, in my patients table, I want to have a column directly for the first name and last name fields (from Person), and so I want to be able to call e.g. firstName (and not having to call Person.firstName) from a query. How may I achieve this?
You can use #Embedded annotation of Room for it.
In your case it will be as follows
public class Person {
String firstName;
String lastName;
}
public class Patient {
int patientId;//just an assumption
#Embedded
Person person;
}
For more information check this
Note : I haven't provided other annotations like #ColumnInfo for brevity
Related
I have two entity order and customer. Each order is associated with zero or single customer and each customer is associated with zero or many orders.
#Entity
public class Order {
#PrimaryKey(autoGenerate = true)
public long id;
public long customerId;
....
}
#Entity
public class Customer {
#PrimaryKey(autoGenerate = true)
public long id;
public String name;
}
I wanted to query the order table with the corresponding customer if it exists.
So, I have created the relation following the documentation.
public class OrderAndCustomer {
#Embedded public Order order;
#Relation(
parentColumn = "customerId",
entityColumn = "id"
)
public Customer customer;
}
I can query the list of orders and the corresponding customer using dao.
#Dao
public interface OrderDao {
#Transaction
#Query("select * from `order`")
List<OrderAndCustomer> getOrderAndCustomer();
}
But when I try to access the columns of child entity, I got compile error. For instance, I wanted to query the order where customer name is like ****.
Therefore, my updated query:
#Query("select * from `order` where customer.name like '****'")
Is it possible to access the attribute of child entity in where clause?
So, the question arises that how the relation works!? I have figure out that it first query the order entity and then query the customer entity. Let me show the correct way If I am wrong.
I have another solution to query multiple tables but I am unable to use the relation feature what room provides or I am missing something!
I can follow this answer to use join and mapping to object.
public class OrderAndCustomer extends Order {
public String customerName;
}
Query:
#Query("select `order`.*, customer.name as customerName from `order` left outer join customer on `order`.customerId = customer.id where customerName like '****'")
List<OrderAndCustomer> getOrderAndCustomer();
But, Still have questions.
How do I map all columns of the order and customer table? Do I need to map all columns of customer with as in the query or there is another simplified approach available? If both tables has more columns and I need all of the columns of the first and second table to be fetched, then my query will be long enough. I wonder if there is the simplest thing to map both tables without or minimal using of as with all columns of the second table.
Help me with the right approach and better available solution.
I have the following tables:
category:
id integer primary key autoincrement;
name text not null;
transaction:
id integer primary key autoincrement;
amount real not null;
category_id integer references category(id);
And have the following entity classes
class Category {
#PrimaryKey int cat_id;
String name;
}
class Transaction {
#PrimaryKey int tx_id;
double amount;
#ForeignKey(entity = Category.class, parentColumns = "category_id", childColumns = "cat_id")
int category_id;
#Embedded Category category;
}
When I run the following query, Transaction.category is always null
select t.* from transaction t JOIN category c ON t.category_id = c.cat_id
So far, most of the tutorials online don't show how to handle this situation. In fact, it also turns out room inserts the #Embedded fields.
How can I structure the entities to make sure a single query to retrieve transactions returns with related categories? The relationship is always one-to-one.
I'm using RxJava and do not want to do second queries. I would like to have a single query return everything because it's displayed in a ReycyclerView.
Thanks
Ok, so I found the answer from here: https://medium.com/androiddevelopers/database-relations-with-room-544ab95e4542 and https://developer.android.com/training/data-storage/room/relationships
So basically, the solution is to use the #Relation annotation , which can only be used on POJOs only as described here:
Please note this is for 1:1 relationships.
A one-to-one relationship between two entities is a relationship where
each instance of the parent entity corresponds to exactly one instance
of the child entity, and vice-versa.
For example, consider a music streaming app where the user has a
library of songs that they own. Each user has only one library, and
each library corresponds to exactly one user. Therefore, there should
be a one-to-one relationship between the User entity and the Library
entity.
First, create a class for each of your two entities. One of the
entities must include a variable that is a reference to the primary
key of the other entity.
#Entity
public class User {
#PrimaryKey public long userId;
public String name;
public int age;
}
#Entity
public class Library {
#PrimaryKey public long libraryId;
public long userOwnerId;
}
In order to query the list of users and corresponding libraries, you
must first model the one-to-one relationship between the two entities.
To do this, create a new data class where each instance holds an
instance of the parent entity and the corresponding instance of the
child entity. Add the #Relation annotation to the instance of the
child entity, with parentColumn set to the name of the primary key
column of the parent entity and entityColumn set to the name of the
column of the child entity that references the parent entity's primary
key.
public class UserAndLibrary {
#Embedded public User user;
#Relation(
parentColumn = "userId",
entityColumn = "userOwnerId"
)
public Library library;
}
Finally, add a method to the DAO class that returns all instances of
the data class that pairs the parent entity and the child entity. This
method requires Room to run two queries, so add the #Transaction
annotation to this method to ensure that the whole operation is
performed atomically.
#Transaction
#Query("SELECT * FROM User")
public List<UserAndLibrary> getUsersAndLibraries();
I have a table that looks like following
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product
{
#PrimaryKey
#ColumnInfo(name = "ID")
#JsonProperty("ID")
public int id;
#ColumnInfo(name = "Name")
#JsonProperty("Name")
public String name;
#ColumnInfo(name = "Documents")
#JsonProperty("Documents")
#TypeConverters(DocumentConverter.class)
public List<Document> documents;
}
//...
#TypeConverters(DocumentConverter.class)
#JsonIgnoreProperties( ignoreUnknown = true )
#JsonTypeName("Documents")
public class Document
{
#JsonProperty("Name")
public String name;
#JsonProperty("URL")
public String url;
}
I am able to retrieve a product based on its name by doing something like this
#Query("SELECT * FROM Product WHERE Name = :name")
List<Product> getProducts(String name);
And I would then be able to access the list of documents from each Product object. However I would also like to only deal with Products that has certain documents. I could get all Products via a query like above, then manually filter for the documents that I want, but it becomes quite a pain when I'm only looking for very specific documents.
Is it possible to also query based on Document variables without it being a separate table?
Something like...
#Query("SELECT * FROM Product WHERE Name = :name AND Document.name = :documentName")
List<Product> getProducts(String name, String documentName);
Thanks.
You could use LIKE sql statement to search inside your json column with converted documents list. Example:
Assume that we have document converted like this for storing in db:
{
name: "Title",
url: "Your_url"
}
So your query for product with such document in list should be like this:
SELECT * FROM Product WHERE Name = :name AND Documents LIKE :documentLikeExpr
Where
String documentLikeExpr = "%name: \"Title\"%";
% in expression mean zero, one or multiple characters.
So the only thing we are doing here - is searching for part of string inside column using SQL language features.
You cannot query a Document class variables as it is not stored as a separate table. #TypeConverter annotation converts your Document list to some predefined data types such as String. Basically it stores list of Document as a string Gson in a column of Product table, So we cannot access the field name of Document class in SQL query like Document.name.
Read the Option #2 given by #CommonsWare here
So, to access it you have to create a separate table for Document.
Let's assume we have following entities:
Item:
class Item {
...
#Index(unique=true)
private String guid;
...
#ToMany
#JoinEntity(entity = JoinItemsWithTags.class, sourceProperty = "itemGuid", targetProperty = "tagName")
private List<Tag> tagsWithThisItem;
...
}
Tag:
class Tag {
#Id
private Long localId;
#Index(unique = true)
private String name;
...
}
and we need to join them. Here is my join entity class:
#Entity(nameInDb = "item_tag_relations")
class JoinItemsWithTags {
#Id
private Long id;
private String itemGuid;
private String tagName;
...
}
I want to use tag name as a join property instead of Long id, because it's easier to support consistency when syncing with server.
But currently tags getter in Item class always return an empty list. I've looked into log and found generated query which using internally in that getter:
SELECT * <<-- there were a long sequence of fields
FROM "tags" T JOIN item_tag_relations J1
ON T."_id"=J1."TAG_NAME" <<-- here is the problem, must be `T."NAME"=J1."TAG_NAME"`
WHERE J1."ITEM_GUID"=?
So the problem is that join is base on tag's _id field. Generated List<Tag> _queryItem_TagsWithThisItem(String itemGuid) method implicitly uses that id to make a join:
// this `join` nethod is overloaded and pass tag's id as source property
queryBuilder.join(JoinItemsWithTags.class, JoinItemsWithTagsDao.Properties.TagName)
.where(JoinItemsWithTagsDao.Properties.ItemGuid.eq(itemGuid));
Correct approach is this case might be following, I suppose:
// source property is passed explicitly
queryBuilder.join(/* Desired first parameter -->> */ TagDao.Properties.Name,
JoinItemsWithTags.class, JoinItemsWithTagsDao.Properties.TagName)
.where(JoinItemsWithTagsDao.Properties.ItemGuid.eq(itemGuid));
But this code is in generated dao, and I don't know how to do anything with it. Is there any way to workaround this?
I want to add an Attachment entity which I will be refering to from multiple different Entities, but it does not refer back to these, how do I get this working in ORMLite?
I keep getting this Exception:
Caused by: java.sql.SQLException: Foreign collection class entity.Attachment for
field 'attachments' column-name does not contain a foreign field named
'attachmentId' of class enity.News
For example I have a News Entity
#DatabaseTable
public class News extends Record {
#DatabaseField(index = true, id = true)
private long newsArticleId;
#DatabaseField
private String subject;
#DatabaseField
private String content;
#ForeignCollectionField
Collection<Attachment> attachments;
}
The Attachment Entity:
#DatabaseTable
public class Attachment extends Record {
#DatabaseField(id = true, index = true)
private long attachmentId;
#DatabaseField
private String attachmentUrl;
}
Could someone please point to me and laugh and tell me why I am doing this wrong and what I'm misunderstanding here. Thanks.
This is a FAQ. To quote from the ORMLite docs on foreign-collections:
Remember that when you have a ForeignCollection field, the class in the collection must (in this example Order) must have a foreign field for the class that has the collection (in this example Account). If Account has a foreign collection of Orders, then Order must have an Account foreign field. It is required so ORMLite can find the orders that match a particular account.
In your example, for ORMLite to figure out which Attachments a particular News entity has, the Attachment entity must have a News field. The other way to do would be to have a join table, but ORMLite won't do that for you.