Today one problem came to me when I was learning Room in Android.
Everything is ok but I just can`t query with where between two tables.
Here are my codes:
......................................................................................................................................................
this code works well
#Query("select users.id,users.name from users where score > :score and assist > :assist")
List<UserSimple> getUserWithLimits(int score,int assist);
but the follow one can`t get anything:
#Query("select users.id,users.name from users,performs where users.id = performs.id and performs.score > :score and performs.assist > :assist")
List<UserSimple> getUserWithLimits(int score,int assist);
and here are my tables created using Room:
........................................................................................................................................................
user table:
#Entity(tableName = "users",
primaryKeys = {"id", "name"},
indices = {
#Index(value = "id", unique = true)
})
public class User {
#android.support.annotation.NonNull
#ColumnInfo(name = "id")
private String id;
#android.support.annotation.NonNull
#ColumnInfo(name = "name")
private String name;
#ColumnInfo(name = "position")
private String position;
#Embedded
private UserPerforms performs;
#Ignore
public User() {
}
public User(String id, String name, String position, UserPerforms performs) {
this.id = id;
this.name = name;
this.position = position;
this.performs = performs;
}
getters/setters/toString()...
}
........................................................................................................................................................
performs table:
#Entity(tableName = "performs",
primaryKeys = "p_id",
foreignKeys = #ForeignKey(entity = User.class
, parentColumns = "id"
, childColumns = "p_id")) //定义主键
public class UserPerforms {
#android.support.annotation.NonNull //
#ColumnInfo(name = "p_id")
private String p_id;
#ColumnInfo(name = "score")
private int score;
#ColumnInfo(name = "assist")
private int assist;
#Ignore
public UserPerforms() {
}
public UserPerforms(String p_id, int score, int assist) {
this.p_id = p_id;
this.score = score;
this.assist = assist;
}
...getters/setters/toString()..
}
........................................................................................................................................................
userSimple class:
public class UserSimple {
#ColumnInfo(name = "id")
private String id;
#ColumnInfo(name = "name")
private String name;
...getters/setters/toString()
public UserSimple(String id, String name) {
this.id = id;
this.name = name;
}
}
Anyone can help me?Thanks in advance.
You are joining wrong columns. Your id column in the performs table is p_id, not id. Try this:
#Query("select users.id,users.name from users,performs where users.id = performs.p_id and performs.score > :score and performs.assist > :assist")
Related
This query is retrieving all the records whatever the date for the pet :
#Query("SELECT * FROM Fooding WHERE pet_Id = :petId AND date(fooding_date) = date(current_timestamp)")
Flowable<List<Fooding>> getDailyFoodingsForPet (Long petId);
Converters :
private static final DateTimeFormatter dtf = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
#androidx.room.TypeConverter
public static OffsetDateTime toOffsetDateTime(String value) {
return value == null ? null : OffsetDateTime.parse(value,dtf);
}
#androidx.room.TypeConverter
public static String fromOffsetDateTime(OffsetDateTime value) {
return value == null ? null : value.format(dtf);
}
Entity :
public class Fooding implements Parcelable {
#NonNull
#PrimaryKey(autoGenerate = true)
private Long foodingId;
#NonNull
#ColumnInfo(name = "user_Id")
private Long userId;
#NonNull
#ColumnInfo(name = "pet_Id")
private Long petId;
#TypeConverters(DataTypeConverter.class)
#ColumnInfo(name = "fooding_date")
private OffsetDateTime foodingDate;
private Integer quantity;
Does anyone have any idea of the problem ?
Edit : this query have the same result :
#Query("SELECT * FROM Fooding WHERE pet_Id = :petId AND date(fooding_date) = date('now')")
Flowable<List<Fooding>> getDailyFoodingsForPet (Long petId);
I have a parent entity Collection:
#Entity(tableName = "collections")
public class Collection implements Serializable {
#PrimaryKey
#NonNull
public String id;
public String collTitle;
public int isFavorite;
public int wordCount;
Collection(String collTitle, int isFavorite, int wordCount) {
this(UUID.randomUUID().toString(), collTitle, isFavorite, wordCount);
}
#Ignore
Collection(#NonNull String id, String collTitle, int isFavorite, int wordCount) {
this.id = id;
this.collTitle = collTitle;
this.isFavorite = isFavorite;
this.wordCount = wordCount;
}
}
and a child entity Word:
#Entity(tableName = "words",
foreignKeys = #ForeignKey(
entity = Collection.class,
parentColumns = "id",
childColumns = "collId",
onDelete = SET_DEFAULT),
indices = #Index("collId"))
public class Word implements Serializable {
#PrimaryKey
#NonNull
public final String id;
public String wordTitle;
public String collId;
public String pageNum;
public int isFavorite;
public String wordNotes;
#Ignore
Word(String wordTitle, String collId, String pageNum, int isFavorite, String wordNotes) {
this(UUID.randomUUID().toString(), wordTitle, collId, pageNum, isFavorite, wordNotes);
}
Word(#NonNull String id, String wordTitle, String collId, String pageNum, int isFavorite,
String wordNotes) {
this.id = id;
this.wordTitle = wordTitle;
this.collId = collId;
this.pageNum = pageNum;
this.isFavorite = isFavorite;
this.wordNotes = wordNotes;
}
}
At the time of inserting a new Collection, I give wordCount as 0 because every new collection would have 0 words associated with it. However, when inserting a new Word, I have to choose one collection from the defined collections and use its 'id' as 'collId' (Foreign Key).
Now I need to retrieve a list of Collection objects with 'wordCount' field containing the actual number of words associated with each Collection object. As already mentioned, 'wordCount' field is assigned a value of 0 at the time of insertion of a Collection object.
I just hope I made my question clear. Kindly suggest a query that serves my purpose. And I don't want to have the result in the form of a new POJO class like, for example, CollectionsAndWords.
Any help would be a great help!
OK, i solved it in the following way:
In my Collection entity, I annotated the wordCount as follows:
#ColumnInfo(name = "word_count")
public int wordCount;
and made changes to the constructors by removing this field:
Collection(String collTitle, int isFavorite) {
this(UUID.randomUUID().toString(), collTitle, isFavorite);
}
#Ignore
Collection(#NonNull String id, String collTitle, int isFavorite) {
this.id = id;
this.collTitle = collTitle;
this.isFavorite = isFavorite;
}
Finally, I added the following query method in my DAO:
#Query("SELECT collections.id, collections.collTitle, collections.isFavorite, " +
"COUNT(words.collId) as word_count " +
"FROM collections " +
"LEFT JOIN words " +
"ON collections.id = words.collId " +
"GROUP BY collections.id " +
"ORDER BY collections.collTitle")
List<Collection> findAllCollsWithCount();
The LEFT JOIN used in the right manner did all the trick for me.
Note that in above query, I'm not retrieving the wordCount field, I'm just letting a new field to be calculated and mapping it to wordCount. If I retrieve wordCount along with other columns from collections table, I get 02 columns that go by the name of word_count; one with 0 value and other with calculated value (COUNT()). I also tried to annotate wordCount with #Ignore but then Room couldn't find any column to map word_count to.
However, for now, the above solution is perfectly serving my purpose, so it is the answer. It might help someone with the same question.
Thanks!
I am trying to get average value if two conditions using Android Room Persistence Library, my entity looks like this:
public class FinishedTask {
private static final String TASK_NAME_COLUMN = "taskName";
private static final String DEPARTMENT_NAME_COLUMN = "departmentName";
private static final String EMPLOYEE_NAME_COLUMN = "employeeName";
private static final String TIME_UNTIL_DOME_NAME_COLUMN = "timeUntilDoneName";
private static final String SAVED_DATE_NAME_COLUMN = "savedDateName";
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
private long id = 0;
#ColumnInfo(name = "passedMilliseconds")
private long passedMilliseconds = 0;
#ColumnInfo(name = TASK_NAME_COLUMN)
private String taskName = TASK_NAME_COLUMN;
#ColumnInfo(name = DEPARTMENT_NAME_COLUMN)
private String departmentName = DEPARTMENT_NAME_COLUMN;
#ColumnInfo(name = EMPLOYEE_NAME_COLUMN)
private String employeeName = EMPLOYEE_NAME_COLUMN;
#ColumnInfo(name = TIME_UNTIL_DOME_NAME_COLUMN)
private long timeUntilDone = 0;
#ColumnInfo(name = SAVED_DATE_NAME_COLUMN)
private long savedDate = 0;
public FinishedTask(){}
}
My Query looks like this:
#Query("SELECT AVG(timeUntilDoneName) FROM finishedTasks WHERE :column1=:value1 AND :column2=:value2")
Single<Long> getAverageTime(String column1, String value1, String column2, String value2);
And I got error:
Query returned empty result set: SELECT AVG(timeUntilDoneName) FROM finishedTasks WHERE ?=? AND ?=?
When I change query to:
#Query("SELECT AVG(timeUntilDoneName) FROM finishedTasks WHERE taskName=:value1 AND :departmentName=:value2")
Single<Long> getAverageTime(String value1, String value2);
Everything works fine and as expected. What am I doing wrong?
You can only use placeholders where SQLite itself supports placeholders, and SQLite does not support replacing column names with placeholders.
I have category class:
#Entity(tableName = "Categories",indices = {#Index(value = {"id"},
unique = true)})
public class Category implements Serializable {
public Category(String name,int icon,int color)
{
_name= name;
_icon = icon;
_color = color;
}
#PrimaryKey(autoGenerate = true)
public int id;
#ColumnInfo(name = "name")
public String _name;
#ColumnInfo(name = "icon")
public int _icon;
#ColumnInfo(name = "color")
public int _color;
public String get_name() {return _name;}
public int getColor() {
return _color;
}
public int getIcon() {
return _icon;
}
}
and I would like to create constant rows only when DB is newly created and not each time the application is loading.
what I did till now is:
protected CategorySingletone() {
// Exists only to defeat instantiation.
AppDatabase db = AppDatabase.getAppDatabase(MainActivity.get().getApplicationContext());
if (db.categoryDao().countCategories()==0) {
_categoryList = new ArrayList<>();
_categoryList.add(new Category("Cars", R.drawable.couch, 3));
_categoryList.add(new Category("Tech", R.drawable.phone, 3));
_categoryList.add(new Category("Home", R.drawable.book_1, 3));
_categoryList.add(new Category("Leisure", R.drawable.book_1, 3));
_categoryList.add(new Category("Motors", R.drawable.book_4, 3));
_categoryList.add(new Category("Fashion", R.drawable.book_5, 3));
for (Iterator<Category> i = _categoryList.iterator(); i.hasNext();) {
Category item = i.next();
db.categoryDao().insertAll(item);
}
}
while I am checking if rows exists.
is there a better way?
A more efficient way would be to use the DatabaseUtils queryNumEntries method.
e.g.
if (DatabaseUtils.queryNumEntries(db,"Categories") == 0) {
.......
}
I'm using ORMLite (v4.48) with my Android app. I have the table "Contact" which can contain multiple "Email" (ForeignCollectionField) and one "Personal" (DatabaseField) object. When I get the Contact object from the database I would like to automatically get (or lazy load) the Personal object which has the same Contact ID.
It already automatically gets the Email objects which I can access. But for some reason the Personal object is always "null" even though there is an entry in the Personal table.
Here are my classes:
#DatabaseTable(tableName = "Contact", daoClass = ContactDao.class)
public class Contact {
#DatabaseField(generatedId = true, columnName = PersistentObject.ID)
int id;
#DatabaseField(index = true)
String contactName;
#ForeignCollectionField(eager = false)
ForeignCollection<Email> emails;
#DatabaseField(foreign = true)
public Personal personal;
public ForeignCollection<Email> getEmails() {
return emails;
}
public void setEmails(ForeignCollection<Email> emails) {
this.emails = emails;
}
public Personal getPersonal() {
return personal;
}
public void setPersonal(Personal personal) {
this.personal = personal;
}
...
}
And
#DatabaseTable(tableName = "Email", daoClass = EmailDao.class)
public class Email {
#DatabaseField(generatedId = true, columnName = PersistentObject.ID)
int id;
#DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = PersistentObject.CONTACT_ID_FIELD_NAME) // contact_id
Contact contact;
#DatabaseField
String emailType;
#DatabaseField(canBeNull = false)
String email;
public Email() {
}
public Email(int id, Contact Contact, String emailType, String email) {
this.id = id;
this.contact = contact;
this.emailType = emailType;
this.email = email;
}
public int getId() {
return id;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public String getEmailType() {
return emailType;
}
public void setEmailType(String emailType) {
this.emailType = emailType;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
...
}
and
#DatabaseTable(tableName = "Personal", daoClass = PersonalDao.class)
public class Personal {
#DatabaseField(generatedId = true, columnName = PersistentObject.ID)
int id;
#DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = PersistentObject.CONTACT_ID_FIELD_NAME)
Contact contact;
#DatabaseField
int age;
#DatabaseField
int weight; // in grams
#DatabaseField
int height; // in cm
public Personal() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
I'm getting the data from the database like this:
QueryBuilder<Contact, Integer> queryBuilder = mContactDao.queryBuilder();
queryBuilder.orderBy("lastViewed", false);
queryBuilder.limit(limit);
PreparedQuery<Contact> preparedQuery = queryBuilder.prepare();
List<Contact> contactList = mContactDao.query(preparedQuery);
that all works well so far.
Then further down the code I can access the Email objects like this:
ForeignCollection<Email> emails = contact.getEmails();
Iterator<Email> iter = emails.iterator();
while (iter.hasNext()) {
Email iAddress = iter.next();
Log.d(TAG, "EMAIL: " + iAddress.getEmail());
Log.d(TAG, "EMAIL TYPE: " + iAddress.getEmailType());
}
Which also works perfectly. Only if I want to access the Personal object I always get NULL.
Personal personal = contact.getPersonal(); // is always NULL
I can't figure out why that is. Do I manually need to add a JOIN in the query builder? I thought it would also lazily load the data once I access it with getPersonal() like it does with getEmails()?
You did not show how entity instances are created, but i assume Personal is created after Contact has been inserted. If that is a case, then after inserting Personal you should do contact.setPersonal(personal), and contactDao.update(contact) - that way personal_id will be stored in contact row