Room (SQLite) WHERE clause with null arguments doesn't work - android

I have entity class Person and I want to query persons from database by feature which may or may not be null. When I set feature to non-null the query works but when the feature is null the query returns empty list. What I am doing wrong?
TEST
Context context = InstrumentationRegistry.getContext();
AppDb db = Room.inMemoryDatabaseBuilder(context, AppDb.class).allowMainThreadQueries().build();
PersonDao dao = db.personDao();
dao.insert(new Person("El Risitas", "Funny"));
dao.insert(new Person("Elon Musk", "Alien"));
dao.insert(new Person("Donald Trump", null));
assertEquals(3, dao.getAll().size());
assertEquals("Funny", dao.getByFeature("Funny").get(0).getFeature());
// fails because dao.getByFeature(null) = EMPTY LIST
assertEquals(null, dao.getByFeature(null).get(0).getFeature());
Person.java
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
#Entity(tableName = "person")
public class Person {
#ColumnInfo(name = "id") #PrimaryKey(autoGenerate = true) private int id;
#ColumnInfo(name = "name") private String name;
#ColumnInfo(name = "feature") private String feature;
public Person(String name, String feature) {
this.name = name;
this.feature = feature;
}
public void setId(int id) { this.id = id; }
public int getId() { return id; }
public String getName() { return name; }
public String getFeature() { return feature; }
}
PersonDao.java
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
#Dao
public interface PersonDao {
#Insert
void insert(Person person);
#Query("SELECT * FROM person")
List<Person> getAll();
#Query("SELECT * FROM person WHERE feature = :feature")
List<Person> getByFeature(String feature);
}
AppDb.java
import androidx.room.Database;
import androidx.room.RoomDatabase;
#Database(
version = 1,
exportSchema = false,
entities = {Person.class}
)
public abstract class AppDb extends RoomDatabase {
public abstract PersonDao personDao();
}

In SQL, nothing is ever equal to null. (Nothing is ever not equal to null, either.) You have to use is null. So your query could be something like (untested)
SELECT * FROM person WHERE feature = :feature or (feature is null and :feature is null)

You can also use IS operator instead of =.
SELECT * FROM person WHERE feature IS :feature
From SQLite documentation:
The IS and IS NOT operators work like = and != except when one or both
of the operands are NULL. In this case, if both operands are NULL,
then the IS operator evaluates to 1 (true) and the IS NOT operator
evaluates to 0 (false). If one operand is NULL and the other is not,
then the IS operator evaluates to 0 (false) and the IS NOT operator is
1 (true). It is not possible for an IS or IS NOT expression to
evaluate to NULL.

Related

Android Room Database How to Filter By String Value in Room

#Dao
public interface DaoAccess {
#Insert
void insertImage(ImagesOfEmployee image);
#Query("Select * from ImagesOfEmployee where id like '%' || :id ||'%'")
String getProfileImagePathByID(String id);
#Query(("Delete from ImagesOfEmployee where id like '%' || :id || '%'"))
void deleteEmployee(String id);
}
#Entity
public class ImagesOfEmployee {
#NonNull
#PrimaryKey
String id;
#ColumnInfo
String path;
public ImagesOfEmployee(String id, String path) {
this.id = id;
this.path = path;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
I don't know how to filter In Room Database Android String Query
Can Any One Know How to do this
I AM attaching the error below. what android studio shows me in the error box
Screen Shot
This is ScreenShot
Android studio error says 'not sure how to convert cursor to the method's return type' as the query with 'Select *' will return the whole record, and I think you are trying to get only path of the image, Just replace * with path as in,
Select path from ImagesOfEmployee where id like '%' || :id ||'%'
hope you are looking for this only.

Room : incorrect result of a query with a date condition

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);

How can I reference variables of generic types?

I am trying to implement a "base DAO" interface for the Room library so as to avoid boilerplate code:
BaseEntity.kt
interface BaseEntity {
val entityName: String
}
Note.kt
#Entity
class Note : BaseEntity {
override val entityName: String = "note"
...
}
BaseDao.kt
interface BaseDao<T : BaseEntity> {
#Query("SELECT * FROM ${T.entityName}")
fun selectAll(): List<T>
...
}
NoteDao.kt
#Dao
interface NoteDao : BaseDao<Note> {
...
}
However, the expression ${T.entityName} is invalid. Is there a way to do this?
I don't believe that you can implement a "base DAO" interface. The reason is that Room creates each DAO implementation at compile time. And hence why you get the message An annotation argument must be a compile time-constant.
Room needs to know, from the annotation (for example), which table columns to map to which variables and the methods used to perform the mapping so that the underlying code can be generated.
As an example if the Entity and the Dao were :-
#Entity
class Note {
#PrimaryKey
var entityName: String = ""
}
and
#Dao
interface BaseDao {
#Query("SELECT * FROM Note")
fun selectAll(): List<Note>
}
Then the underlying generated java would be :-
public final class BaseDao_Impl implements BaseDao {
private final RoomDatabase __db;
public BaseDao_Impl(RoomDatabase __db) {
this.__db = __db;
}
#Override
public List<Note> selectAll() {
final String _sql = "SELECT * FROM Note";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfEntityName = CursorUtil.getColumnIndexOrThrow(_cursor, "entityName");
final List<Note> _result = new ArrayList<Note>(_cursor.getCount());
while(_cursor.moveToNext()) {
final Note _item;
_item = new Note();
final String _tmpEntityName;
_tmpEntityName = _cursor.getString(_cursorIndexOfEntityName);
_item.setEntityName(_tmpEntityName);
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
}

How to obtain count of parent foreign key from child table using Room

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!

Can`t query using 'where' between two tables in one DB?

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")

Categories

Resources