I'm trying to obtain the result of a subtraction between two rows in the database. Users specify the conditions on spinners (populated with the "model" column), press a button and the query is launched.
Spinners are properly saving the position into sharedpreferences and later obtaining it.
Button function:
public int value;
//later on
TextView converter = findViewById(R.id.converter);
AppExecutors.getInstance().diskIO().execute(() -> {
LiveData<Integer> value = mDb.personDao().loaddifferconstants(spinA, spinB);
converter.setText(""+value); //quick dirty method
});
Dao
#Query("SELECT t1.const - t2.const AS result FROM person t1 JOIN person t2 WHERE t1.model == :spinA AND t2.model == :spinB")
LiveData<Integer> loaddifferconstants(String spinA , String spinB);
The query does work in DBBrowser, as a direct sql query. So I guess the error lies on how the result is processed into an integer. I tried listing the result, using both livedata integer, int, list... trying to pass it as a String... Failed.
Update 1:
Integer doesn't work either.
Actually Integer count doesn't work either, with the Dao being
#Query("SELECT COUNT(*) FROM PERSON")
int count();
Thank you
LiveData<Integer> value = mDb.personDao().loaddifferconstants(spinA, spinB);
converter.setText(""+value); //quick dirty method
value is a LiveData. This will cause the query to be executed asynchronously. By the next statement, that query will not have completed, and the LiveData will not have the query result.
Either:
Remove LiveData from loaddifferconstants() and have it simply return Integer, so the query will be executed synchronously, or
Consume the LiveData properly, by registering an observer
Since you seem to by trying to call those two lines inside your own background thread, I recommend the first approach: get rid of the LiveData. That would give you:
#Query("SELECT t1.const - t2.const AS result FROM person t1 JOIN person t2 WHERE t1.model == :spinA AND t2.model == :spinB")
Integer loaddifferconstants(String spinA , String spinB);
I want to get the integer value from my Expense table which is a sum of the expenses(amount column) total expense.
In my Expense Entity class
//This is the column Amount which I want the sum of all values in this column and get single integer value total amount
#ColumnInfo(name = "Amount")
private int amount;
public int getAmount() {
return amount;
}
In my Dao class here is the function which give me total amount through LiveData
#Query("SELECT SUM(Amount) from Expense_table")
LiveData<Integer> getTotalExpenseAmount();
This is from Repository class
public LiveData<Integer> getTotalExpenseAmount() {
return expenseDao.getTotalExpenseAmount();
}
And this is from ViewModel class
public LiveData<Integer> getTotalExpenseAmount() {
return expenseRepository.getTotalExpenseAmount();
}
Everything works fine as it should be. But in my case I don't want to get this through liveData observer, instead I want to get this total amount value itself. Where I don't have to use the observer again and again
expenseViewModel.getTotalExpenseAmount().observe(MainActivity.this, new Observer<Integer>() {
#Override
public void onChanged(Integer integer) {
totalExpense = integer;
addDataSet();
Toast.makeText(MainActivity.this, "Total Expense " + totalExpense, Toast.LENGTH_SHORT).show();
}
});
I've to get this total amount value in many classes where I don't want to observe this again and again. Please tell me any shortcut to do this efficiently.
I'll be very thankful to you guys if you solve my problem
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)
I am trying to change my sqlite database with room library. I am little confuse with left join query.
I have implemented it with sqlite, but don't know how can I achieve same withh room?
Here is my table creation:
first table: Notification
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_NAME ($COLUMN_ID INTEGER PRIMARY KEY, $ICON TEXT, $TITLE INTEGER," +
" $DATE INTEGER, $TYPE INTEGER,$URL TEXT, $MESSAGE INTEGER, FOREIGN KEY($TITLE) REFERENCES ${TableNotificationsTrans.getTableName(this)}(id)," +
"FOREIGN KEY($MESSAGE) REFERENCES ${TableNotificationsTrans.getTableName(this)}(id))")
second table: Notification_Trans
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_NAME ($COLUMN_ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, $COLUMN_EN TEXT, $COLUMN_GU TEXT, $COLUMN_HI TEXT)")
What I am doing is I am storing notification in notification table but its name and description will be stored with specific language, in notification_trans.
Query to achieve
DatabaseHelper.database!!.rawQuery("SELECT A.$COLUMN_ID, A.$ICON, N.${language.toLowerCase()} $TITLE, A.$DATE, A.$TYPE, A.$URL, M.${language.toLowerCase()} $MESSAGE FROM $TABLE_NAME A LEFT JOIN NotificationsTrans N ON A.$TITLE = N.id LEFT JOIN NotificationsTrans M ON A.$MESSAGE = M.id ORDER BY $DATE DESC LIMIT $pageNum*10, 10", null)
Question
How can I achieve same with room?
Edit
My application is multi-language application, where I am getting notification title with specific language, like Hindi or Gujarati. I am storing notification details in notification table, while title in notification_trans.
NotificationTrans have column with id, english, hindi, gujarati.
When user asked for gujarati, I am retriving notification title from notificationTrans's column gujarati.
I am able do so, in sqlite.
But now I want it with Room
First You have to make the model classes for both, You may have already declared them , You just need to make a few changes if they already exists.
#Entity
public class Notification {
#PrimaryKey
int id;
String icon;
#ForeignKey(entity = Notification_Trans.class, parentColumns = "col_id", childColumns = "id")
String title;
int date;
int type;
String url;
int msg;
}
#Entity
public class Notification_Trans {
#PrimaryKey(autoGenerate = true)
int col_id;
String column_en;
String column_gu;
String column_hi;
This makes for your POJO, I couldn't understand your Foreign key constraints, so Pardon me for that, you can make changes as you see fit.
You can Declare your DAO as per this`
#Dao
public interface DAO {
#Query("SELECT note.id, note.title, note.description, category.name as categoryName " +
"FROM note " +
"LEFT JOIN category ON note.category_id = category.id")
List getCategoryNotes();
}
`
I haven't made changes in the query, which I found at Link here. As your query was a complex one, But, it'll give you an Idea about how to do that.,
After this, You just need to access your Dao interface from your Database class object, which will handle the creation & all other things room, like this one below`
#Database(entities = {Notification.class, NotificationTrans.class}, version = 3)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase instance;
public static AppDatabase getAppDatabase(Context context) {
if (instance == null) {
instance =
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "database_name")
// allow queries on the main thread.
// Don't do this on a real app! See PersistenceBasicSample for an example.
//.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
public static void destroyInstance() {
instance = null;
}
public abstract Dao notificationDao();
It helps creating a separate class for Database, & keeping track of object from it.
& you can access your data with AppDatabase.getAppDatabase(context).notificationDao().yourQueryMethodName();
You may require to refer to this to understand the relations between room, & implement your requirement,
EDIT 1:
Here's how your DAO should look like ,`
#Insert
void insert(Notifications object);
//This will insert a single Item
#Insert
void insertAll(Notifications... objects);
While this can enter a list of Data,
You can call this methods with your Database object, likeAppDatabase.getAppDatabase(context).notificationDao().yourQueryMethodName() here instead of yourQueryMethod(), if you call insert() & pass the object you need to store in the database, It'll do it,
For E.g.db.parcelDao().insert(parcel);
this is how I insert Data in my ParcelDao, db is Database object, & parcel is the object of data need to be stored. One more thing, you can't call this method on main thread, so you may need to use Handler or AsyncTask for the purpose, Sorry I forgot to mention that.
Have a look at Room Training at Android Developers for implementation of basic functionality of room
#1 - Need to create a model class that matches with the result of the query
data class ClientAndCity(
#ColumnInfo(name="id") val id: Long,
#ColumnInfo(name="client_name") val clientName: String?,
#ColumnInfo(name="city_name") val cityName: String?
)
#2 - Inside your DAO create your query
#Query("SELECT clients.id, clients.name AS client_name, cities.name AS city_name FROM clients LEFT JOIN cities ON cities.id = clients.city_id WHERE clientes.id = :clientId")
fun getClientAndHisCity(clientId: Long): ClientAndCity?
#3 - Use you function
CoroutineScope(Dispatchers.IO).launch{
val result: ClientAndCity = clientDAO.getClientAndHisCity(clientId)
//do something with it
}
I am learning to fetching data from sqlite using anko. I can print the data successfully (if the record exist) but my application always crash when the data doesn't exist.
the error says:
parseSingle accepts only cursors with a single entry
I know exactly the meaning of error, I just dont know how to solve it.
here is the code for query:
fun getUserByUid(uid: Int): UserModel
{
val data = context.database.use {
val db = context.database.readableDatabase
val columns = UserModel.COLUMN_ID + "," + UserModel.COLUMN_NAME + "," + UserModel.COLUMN_API_KEY
val query = db.select(UserModel.TABLE_NAME, columns)
.whereArgs("(uid = {userId})",
"userId" to uid)
query.exec {
val rowParser = classParser<UserModel>()
parseSingle(rowParser) // this line that trigger error exception
}
}
return data
}
I tried to find count function in query or rowParser variable to check if the record exist or not but could not find it.
From the wiki page.
https://github.com/Kotlin/anko/wiki/Anko-SQLite#parsing-query-results
Parsing query results
So we have some Cursor, and how can we parse it into regular classes? Anko provides functions parseSingle, parseOpt and parseList to do it much more easily.
Method Description
parseSingle(rowParser): T Parse exactly one row
parseOpt(rowParser): T? Parse zero or one row
parseList(rowParser): List Parse zero or more rows
Note that parseSingle() and parseOpt() will throw an exception if the received Cursor contains more than one row.