Android Room DB - use static variable from another class in query - android

When I write a query in DAO class can I use a static variable from another class (or enum)? If it's possible, please tell me HOW?
I mean something like this:
public enum MessageState {
NOTHING,
PENDING,
SEND
}
and in query statement in DAO class:
#Query("SELECT * FROM message_db WHERE state = :MessageState.PENDING.ordinal()")

Pass your enum to the function as a default parameter:
#Query("SELECT * FROM message_db WHERE state = :state")
fun getMessages(state: Int = MessageState.PENDING.ordinal()): List<Message>
This way you won't need to provide it on every call, but achieve the same result.

You can concatenate it just like a normal String, in Java:
#Query("SELECT * FROM message_db WHERE state = " + MessageState.PENDING.ordinal() + ")"
If you are using Kotlin:
#Query("SELECT * FROM message_db WHERE state = ${MessageState.PENDING.ordinal()}")

Related

LiveData and room using inheritance

I have an app Im trying to write that has 3 objects that inherit from one object, all entities,
Im using room to store those locally.
In the dao of each entity I have "getAll" function that returnes a livedata<List>.
My question is,
is there a way, to get all of the lists from the database as one list (since they all inherit from the same class)?
Unless Im wrong, if I'll just use "getAll" on the superclass it wont give me the specific fields for every class.
and I have one recyclerView that holds those objects as 1 list so I need a way to combine them.
I tried looking it up but when it comes to inheritance its not really clear how Room handle stuff.(for example in the documentation google gives an example using inheritance with both objects having uniqe id's, but when i tried i got an error that the superclass id will be overwritten by the subclass id.).
If anyone could help, or provide a link to where i can learn more about it I'll greatly appriciate it.
Thanks, and have a great day!
Leaving this here in-case someone else needs it.
There are multiple ways to go about solving this one.
The first one is using a POJO as "MikeT" stated on his answer.
The second one is adding a "type" property to the superclass and get the whole
superclass list, and on the runtime select the proper object and create it.(using
the id since its the same).
the downside is that you access the db multiple times which can reduce
performance. (the solution I was going for before this morning)
The third way(that I ended using) is in this post answer by "Danail Alexiev"
Polymorphic entities in Room
creating a custom MediatorLiveData implementation that zipps the 2 (or more)
livedata objects and returns one.
I believe that you could use a POJO (or perhaps a suitable Entity if one exists) that includes ALL fields and utilise a getAll #Query that includes a UNION. Of course the better way is to perhaps reconsider the design.
The following is an example of a Parent (BaseObject) from which 2 Objects are inherited, name ChildType1 and ChildType2.
In this example a ChildType2 to has 2 additional fields one of which a ChildType1 has as it's only additional field. Hence a ChildType2 is suitable for holding all the fields of a ChildType1.
However, to enable a ChildType1 to be correctly extracted it has to mimic the additional field of the ChildType2. This can be done easily with the SQL in the getAll() method in the Dao Alldao.
The following is the code utilised:-
BaseObject from which the two ChildTypes inherit:-
class BaseObject {
#ColumnInfo(name = BaseColumns._ID)
Long id;
String name;
long createdTimestamp = System.currentTimeMillis() / 1000;
int type;
}
ChildType1 :-
#Entity(primaryKeys = {BaseColumns._ID})
class ChildType1 extends BaseObject {
public static final int TYPE = 1;
String ct1;
}
As will be seen the id column (_id) has been inherited and later that it causes no issues.
ChildType2 :-
#Entity(primaryKeys = {BaseColumns._ID})
class ChildType2 extends BaseObject {
public static final int TYPE = 2;
String ct1;
String ct2;
}
AllDao where All the Dao's have been coded :-
#Dao
interface AllDao {
#Insert
long insert(ChildType1 childType1);
#Insert
long insert(ChildType2 childType2);
#Query("SELECT *, 'n/a' AS ct2 FROM ChildType1 UNION SELECT * FROM childtype2")
List<ChildType2> getAll();
}
The query being using a UNION of initially the childtype1 table filling in the missing ct2 field with the value n/a and the childtype2 table. Note that id's will probably be duplicated so to utilise an id you would have to determine the respective type (e.g. is ct2 = n/a then it's probably a ChildType1 (hence why I'd string suggest an indicator of the type which cannot be ambiguous)).
The #Database TheDatabase :-
#Database(entities = {ChildType1.class,ChildType2.class},version = 1)
abstract class TheDatabase extends RoomDatabase {
abstract AllDao getAllDao();
private volatile static TheDatabase instance;
public static TheDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase.class,
"thedatabase.db"
)
.allowMainThreadQueries()
.build();
instance.getOpenHelper().getWritableDatabase();
}
return instance;
}
}
And finally an Activity, MainActivity putting it all together to demonstrate :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
AllDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = TheDatabase.getInstance(this);
dao = db.getAllDao();
ChildType1 c1_1 = new ChildType1();
c1_1.name = "CT1 001";
c1_1.ct1 = "This is a CT1 for CT1 001";
c1_1.type = ChildType1.TYPE;
dao.insert(c1_1);
ChildType2 c2_1 = new ChildType2();
c2_1.name = "CT2 002";
c2_1.ct1 = "This is CT1 for CT2 002";
c2_1.ct2 = "This is CT2 for CT2 002";
dao.insert(c2_1);
for(ChildType2 c: dao.getAll()) {
Log.d("" +
"TYPEINFO",
"Name = " + c.name +
"\n\t Created = " + c.createdTimestamp +
"\n\t ID = " + c.id +
"\n\t type = " + c.type +
"\n\t CT1 = " + c.ct1 +
"\n\t CT2 = " + c.ct2
);
}
}
}
Result
When run the log contains :-
D/TYPEINFO: Name = CT1 001
Created = 1626589554
ID = 1
type = 1
CT1 = This is a CT1 for CT1 001
CT2 = n/a
D/TYPEINFO: Name = CT2 002
Created = 1626589554
ID = 1
type = 0
CT1 = This is CT1 for CT2 002
CT2 = This is CT2 for CT2 002
i.e. both types of children have been extracted into a single List of objects that can contain ALL the fields.

Android SQLite dynamically insert sorting (ASD or DESC), is it possible?

I'm trying to use one method for dao (Room) to get items by a specific sorting dynamically, but compiler gives an error for the next SQL query
So it's not possible? Do I have to create duplicate methods with different sorting?
you can not perform if and else logic in the sqlite query
You should use #RawQuery like this:
#Dao
interface RawDao {
#RawQuery
fun getTestItems(SupportSQLiteQuery query): DataSource.Factory
}
// Usage of RawDao
// for example set: restOfQuery = sortBy + "ASC"
val query = SimpleSQLiteQuery(
"SELECT * FROM Items ORDER BY ?",
new Object[]{ restOfQuery });
val result = rawDao.getTestItems(query);
Or another way is that you use multiple functions for multiple orderings.

Is it possible to enable/ disable transformation on selected Room Database Dao column?

Currently, we have the following database table
#Entity(
tableName = "note"
)
public class Note {
#ColumnInfo(name = "body")
private String body;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
The length of the body string, can be from 0 to a very large number.
In certain circumstance, we need to
Load the all notes into memory.
A LiveData which is able to inform observers, if there's any changes made in the SQLite note table.
We just need the first 256 characters of body. We do not need entire body. Loading entire body string for all notes might cause OutOfMemoryException.
We have the following Room Database Dao
#Dao
public abstract class NoteDao {
#Query("SELECT * FROM note")
public abstract LiveData<List<Note>> getAllNotes();
}
getAllNotes able to fulfill requirements (1) and (2), but not (3).
The following getAllNotesWithShortBody is a failed solution.
#Dao
public abstract class NoteDao {
#Query("SELECT * FROM note")
public abstract LiveData<List<Note>> getAllNotes();
#Query("SELECT * FROM note")
public abstract List<Note> getAllNotesSync();
public LiveData<List<Note>> getAllNotesWithShortBody() {
MutableLiveData<List<Note>> notesLiveData = new MutableLiveData<>();
//
// Problem 1: Still can cause OutOfMemoryException by loading
// List of notes with complete body string.
//
List<Note> notes = getAllNotesSync();
for (Note note : notes) {
String body = note.getBody();
// Extract first 256 characters from body string.
body = body.substring(0, Math.min(body.length(), 256));
note.setBody(body);
}
notesLiveData.postValue(notes);
//
// Problem 2: The returned LiveData unable to inform observers,
// if there's any changes made in the SQLite `note` table.
//
return notesLiveData;
}
}
I was wondering, is there any way to tell Room database Dao: Before returning List of Notes as LiveData, please perform transformation on every Note's body column, by trimming the string to maximum 256 characters?
Examining the source code generated by Room Dao
If we look at the source code generated by Room Dao
#Override
public LiveData<List<Note>> getAllNotes() {
final String _sql = "SELECT * FROM note";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
...
...
final String _tmpBody;
_tmpBody = _cursor.getString(_cursorIndexOfBody);
_tmpPlainNote.setBody(_tmpBody);
It will be great, if there is a way to supply transformation function during runtime, so that we can have
final String _tmpBody;
_tmpBody = transform_function(_cursor.getString(_cursorIndexOfBody));
_tmpPlainNote.setBody(_tmpBody);
p/s Please do not counter recommend Paging library at this moment, as some of our features require entire List of Notes (with trimmed body String) in memory.
You can use SUBSTR, one of SQLite's built-in functions.
You need a primary key in your #Entity. Assuming that you call it id, you can write a SQL like below.
#Query("SELECT id, SUBSTR(body, 0, 257) AS body FROM note")
public abstract LiveData<List<Note>> getAllNotes();
This will return the body trimmed to 256 chars.
With that being said, you should consider segmenting your rows. If you have too many rows, they will eventually use up your memory at some point. Using Paging is one way to do it. You can also use LIMIT and OFFSET to manually go through segments of rows.

How to get specific column using Android Room

I'm trying to get id column from my database, ad it to ArrayList and to each id add "\t0",
My database is created using Room, i have a lot of column which one of them is
#PrimaryKey(autoGenerate = true)
private int id;
I am operating using ItemDAO and i have there function
#Query("SELECT * FROM item")
List<Item> getItems();
Which writes to ArrayList<Items> all of contents
I was thinking of running it trough the loop getting id and adding to ArrayList<String> but this doesn't seems to be eficient.
Your DAO:
#Query("SELECT Id FROM item")
List<Integer> getAllIds();
Your model:
#ColumnInfo(name = "Id")
#PrimaryKey(autoGenerate = true)
private int id;
In you query SELECT * FROM item * means select All, put there your column name and you will get list of objects from that column
Example: Select all items in id column SELECT id FROM item
I tried to modify and test #Valgaal 's solution. It turns out that Room can also return other type of values, more than just id (or integer).
For example, you can write an item class like this:
#Entity(tableName = Item.TABLE_NAME)
public class Item {
public static final String TABLE_NAME = "ItemsTable";
public static final String COL_DESC = "Description";
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = COL_DESC)
private String description;
// getter & setter...
}
And then, you can write Dao like this:
#Dao
public interface ItemDao {
#Query("SELECT * FROM " + Item.TABLE_NAME)
List<Item> getItems();
#Query("SELECT " + Item.COL_DESC + " FROM " + Item.TABLE_NAME)
List<String> getItemDescriptions();
}
And it's functional as it should be.
I guess all of the other data types that Room can save (including custom types?) can be queried (and returned lists of specific column data) by the same logic above. Hope this would help someone in the future!
For returning multiple columns, create a pojo class that can be set as a return type for your DAO function
Note the select query should contain the Pojo class variable name (can be done via AS keyword)
Detailed answer here
https://stackoverflow.com/a/50802209/1029110
I landed on this question for my issue...but didnt find answer. So this may help others.

Dynamic Order By using Room

I want to create a dynamic query using room so that in one case the query returns a particular order type and during runtime if the order type changes then a new query is created and the data is returned ordered according to this type.
I am returning a DataSource.Factory object using Room.
I am using the below statement to process my query:-
if(getSortOrderType().equals(context.getString(R.string.sortByPopular))) {
dbSortType = "popularity";
} else {
dbSortType = "vote_average";
}
movieLiveData =
new LivePagedListBuilder<>(MovieDatabase
.getMovieDbInstance(context)
.getMovieDao().getAllMovies(new
SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY ? DESC",
new Object[]{dbSortType})), config)
.setBoundaryCallback(movieModelBoundaryCallback)
.build();
But, during runtime I see that the data being returned is ordered by the already set Primary Key i.e id and not according to this type that I am constructing in the above statement.
How to use the statement to return the result sorted by the sort type selected.
The Dao method used is:-
#RawQuery(observedEntities = MovieModel.class)
DataSource.Factory<Integer,MovieModel> getAllMovies(SupportSQLiteQuery query);
Okay so, I have found a simple answer to that.
I just replaced the statement --
new SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY ? DESC", new Object[]{dbSortType})
to this:-
new SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY "+ dbSortType + " DESC"))
First of all check that getAllMovies function in your DAO is like this:
#RawQuery
fun getAllMovies(query: SupportSQLiteQuery): List<MainMovie>
and if your return type is observable types like liveData, PagingSource, etc. :
#RawQuery(observedEntities = [MainMovie::class])
fun getAllMovies(query: SupportSQLiteQuery): PagingSource<Int, MainMovie>
then use this:
val myQuery=SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY "+ dbSortType + " DESC")
getMovieDao(). getAllMovies(myQuery)

Categories

Resources