In Android Room how can I create and call a custom query - android

I've set up a room database with 3 columns (title, descriptions, genre). I want to query the genre column with a user-specified genre(comedy, horror, etc) and return the results.
DAO Interface
I want the Query to only retrieve the entries where the genre matches the genre selected by the user.
#Dao
public interface MovieDAO {
#Query SELECT * FROM movie_table WHERE genre")
pubic LiveData<List<Movie>> getAllMovies();
}
Repository Class
In the Repository.class, can I pass the genre String selected by the user to the Query this way?
public class MovieRepository {
private MovieDao movieDao;
private LiveData<List<Movie>> allMovies;
public MovieRepository(Application application) {
MovieDatabase database = MovieDatabase.getInstance(application);
movieDao = database.MovieDao();
allMovies = movieDao.getAllMovies
}
public void findMoviesByGenre(String genre) {
movieDao.findMoviesByGenre(genre);
}
}
ViewModel class
I'm not sure if I'm missing something in the findMovieByGenre() method
public class MovieViewModel exteneds AndroidViewModel {
private MovieRepository repository;
private LiveData<List<Movie>> allMovies
// Constructor,
public MovieViewModel(#NonNull Application application) {
super(application);
repository = new MovieRepository(Application)
allMovies = repository.getAllMovies();
}
**public void findMovieByGenre(String genre) {
repository.findMoviesByGenre(genre);
}**
}
Activity
This is the part I'm really struggling with, how does the activity call the ViewModel and pass in the genre string parameter? I've tried the approach below but the observe returns the following error.
Cannot resolve method 'observe(com.example.roomexample.MainActivity, anonymous android.arch.lifecycle.Observer>)'
If I remove the genre string in from of the observe, I get the error below.
findMovieByGenre(String)in MovieViewModel cannot be applied
to () 
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
movieViewModel = ViewModelProviders.of(this). get(MovieViewModel.class);
movieViewModel.findMovieByGenre("comedy").observe(this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable final List<Movie> movies) {
Log.d("Movie", movies.get(num).getTitle());
Log.d("Movie", movies.get(num).getDescription());
Log.d("Movie", movies.get(num).getGenre());
}
});
}
In short I want to match the genre selected by the user and match it to the genre entry in the database and return the matching results.
My code is based on the following tutorials. If you have any additional material that code help me in my quest please pass it along.
Google coding Labs
Coding in Flow
Here is a link to my code as it currently stands.
https://github.com/Shawn-Nichol/RoomExample

If you are using MVVM architecture with LiveData follow this method.
1. Observe the LiveData List in MoviesActivity.java
final LiveData<List<MoviesData>> viewModelData = moviesViewModel.getMoviesByGenre("comedy");
viewModelData.observe(this, new Observer<List<MoviesData>>() {
#Override
public void onChanged(List<MoviesData> moviesData) {
//Handle the Movies List here.
}
});
2. In MoviesViewModel.java
public LiveData<List<NotificationData>> getMoviesByGenre(String genere) {
MoviesRepository mRepository = new MoviesRepository(application);
LiveData<List<MoviesData>> mMoviesData = mRepository.getMoviesByGenre(genere);
return mMoviesData;
}
3. MoviesRepository.java
private MoviesDao mMoviesDao;
//Initialize.
AppDatabase db = AppDatabase.getAppDatabase(application);
mMoviesDao = db.moviesDao();
public LiveData<List<MoviesData>> getMoviesByGenre(String genere) {
mMovies = mMoviesDao.findMovieByGenre(genere);
return mMovies;
}
3. In MoviesDao
#Query("SELECT * FROM movie_table ORDER BY genre")
public LiveData<List<Movies> findMovieByGenre(String genre);
So you can Observe query result in your Activity class' Observe method.

To access your app's data using the Room persistence library, you work with data access objects, or DAOs. You may have to use DAO in android room.
By accessing a database using a DAO class instead of query builders or direct queries, you can separate different components of your database architecture
#Dao
public interface MyDao {
#Query("SELECT * FROM movie_table ORDER BY genre")
public ListMovies[] findMovieByGenre(String Genre);
}

Related

How to derive a ViewModel for an object attribute in another ViewModel?

I want to get a ViewModel for an attribute of another object held in another ViewModel.
I have this relationship: In a house there are multiple people. (A 1:n relationship where people are encoded in the Houses table, rather than using a join table.) I have a problem in this scenario:
An existing House is to be shown in HouseDetailsActivity, which contains a HouseDetailsFragment and a PeopleListFragment. The HouseDetailsActivity gets the HouseViewModel in onCreate, like this:
houseViewModel = ViewModelProviders.of(this, new HouseViewModel.Factory(getApplication(), id)).get(HouseViewModel.class);
The HouseViewModel is able to return LiveData, as it gets the HouseEntity from the database. The PeopleListFragment needs to get LiveData for the list of people for that house from somewhere, but should not need knowledge of any view model other than PeopleListViewModel. So, also in the HouseDetailsActivity onCreate, I get a PeopleListViewModel, like this:
peopleListViewModel = ViewModelProviders.of(this).get(PeopleListViewModel.class);
that I expect can be shared with the PeopleListFragment, getting it like this:
peopleViewModel = ViewModelProviders.of(getActivity()).get(PeopleListViewModel.class);
The problem is how to get the list of people in LiveData into the ViewModel. The list of people in the HouseEntity inside the HouseDetailsActivity (HouseDetailsViewModel) is not LiveData. (I want to be able to see the list of people from the HouseEntity in the PeopleListFragment via a PeopleListViewModel.)
I've seen the documentation for MediatorLiveData, which I don't think applies here, because ultimately there is only 1 source of the PeopleList.
public class HouseDetailsActivity
{
protected void onCreate(Bundle savedInstanceState)
{
houseViewModel = ViewModelProviders.of(this, new HouseViewModel.Factory(getApplication(), id)).get(HouseViewModel.class);
peopleListViewModel = ViewModelProviders.of(this).get(PeopleListViewModel.class);
/* This can't be done, because the HouseEntity may not yet be loaded to the ViewModel. ie. NullPointerException here
List<Person> people = m_houseViewModel.getHouse().getPeopleList();
peopleListViewModel.setPeople(people);
*/
}
}
#Entity(tableName="houses")
public class HouseEntity implements MutableHouse
{
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name="hid")
public int id = 0;
#ColumnInfo(name="address")
private String address = null;
/** This is the encoded people, for multiple in a single database field. */
#ColumnInfo(name="residents")
private String residents = null;
public List<Person> getPeopleList ()
{ return HouseEncoding.decodePeople(getResidents()); }
...
}
public class HouseViewModel
{
private final int houseId;
private MutableLiveData<HouseEntity> house; // The list of people is inside house here, but not as LiveData
public LiveData<HouseEntity> getObservableHouse ()
{ return house; }
HouseViewModel (#NonNull Application application, int houseId)
{
super(application);
this.houseId = houseId;
this.house = getRepository().getHouseObservable(houseId);
}
/**
* A creator is used to inject the house ID into the ViewModel
*/
public static class Factory extends ViewModelProvider.NewInstanceFactory
{
#NonNull
private Application application;
private int houseId;
public Factory (#NonNull Application application, int houseId)
{
this.application = application;
this.houseId = houseId;
}
#Override
#NonNull
public <T extends ViewModel> T create (#NonNull Class<T> modelClass)
{
//noinspection unchecked
return (T) new HouseViewModel(application, houseId);
}
}
}
public class PeopleListViewModel
{
private MutableLiveData<List<Person>> people;
void setPeople (List<Person> people)
{ this.people.setValue(people); }
...
}
Within the PeopleListFragment:
private void observerSetup ()
{
peopleViewModel.getPeople().observe(this, people -> {
adapter.setPeople(people); // for RecyclerView
});
}
I see you have initialized these two viewmodels differently.
houseViewModel = ViewModelProviders.of(this, new HouseViewModel.Factory(getApplication(), id)).get(HouseViewModel.class);
peopleListViewModel = ViewModelProviders.of(this).get(PeopleListViewModel.class);
Just change:
houseViewModel = ViewModelProviders.of(this, new HouseViewModel.Factory(getApplication(), id)).get(HouseViewModel.class);
to this:
houseViewModel = ViewModelProviders.of(this, ViewModelProviders.of(this).get(HouseViewModel.class);
You can initialize two viewmdoels in a view. Don't be shy about it.

Android room - id of insterted row - from ViewModel

I am using Android Room, and I would like to get ID of new inserted row. I have declared column in my model class:
#PrimaryKey (autoGenerate = true)
#ColumnInfo (name = "productID")
int id;
And then I know I can retrive it by dao returning long:
#Insert
long insert(Product p);
At first I was using "thread" calls directly in View. And as you know, it is not recommended method. So I am trying to change it for ModelView and repository. But I don't know how can I get this ID.
My repository class:
public class ProductRepository {
private ProductDao mProductDao;
ProductRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mProductDao = db.pDao();
}
public void insertProduct(Product p) {
new insertAsyncTask(mProductDao).execute(p);
}
private static class insertAsyncTask extends AsyncTask<Product, Void, Void> {
private ProductDao mAsyncTaskDao;
insertAsyncTask(ProductDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final Product... params) {
mAsyncTaskDao.insert(params[0]);
return null;
}
}
}
And my model class:
public class ProductModelView extends AndroidViewModel {
private ProductRepository mRepository;
public ProductModelView(Application application) {
super(application);
mRepository = new ProductRepository(application);
}
public void insert(Product p) {
mRepository.insertProduct(p);
}
}
And in my Activity I am inserting new object like this:
mProductModelView.insert(pc);
So how I can retrive this long value from "insert" and get it in my activity? I guess LiveData could be a good way to go, but to be honest I dont havy any ideas how to achieve it :(
The best way to do this is by using LiveData. If you want to use MVVM might as well learn how to use LiveData. It's easy.
In your DAO interface, declare a method like this:
#Query("SELECT * FROM Product ORDER BY id DESC LIMIT 1")
LiveData<Product> getLastProductLive();
This method returns the last Product inserted as LiveData
Then inside your Repository:
public LiveData<Product> getLastProductLive(){
return mProductDao.getLastProductLive();
}
And then inside your ViewModel:
public LiveData<Product> getLastProductLive(){
return mRepository.getLastProductLive();
}
And finally inside your Activity:
mProductViewModel.getLastProductLive().observe(this, product -> {
long lastInsertedRowId = product.getId();
}
By using LiveData, any time that a product is added to table, it triggers this method and you can get the id of the last inserted row.

Android Architecture Components LiveData

I'm trying to implement a simple App using Architecture Components.
I can get the info from RestApi services using Retrofit2.
I can show the info in the respective Recyclerview and when I rotate the phone everything works as it should.
Now I want to filter by a new kind of object (by string)
Can someone guide me a little with the ViewModel, I don't know what is the best practice to do that...
I'm using MVVM...
This is my ViewModel:
public class ListItemViewModel extends ViewModel {
private MediatorLiveData<ItemList> mList;
private MeliRepository meliRepository;
/* Empty Contructor.
* To have a ViewModel class with non-empty constructor,
* I have to create a Factory class which would create instance of you ViewModel and
* that Factory class has to implement ViewModelProvider.Factory interface.
*/
public ListItemViewModel(){
meliRepository = new MeliRepository();
}
public LiveData<ItemList> getItemList(String query){
if(mList == null){
mList = new MediatorLiveData<>();
LoadItems(query);
}
}
private void LoadItems(String query){
String queryToSearch = TextUtils.isEmpty(query) ? "IPOD" : query;
mList.addSource(
meliRepository.getItemsByQuery(queryToSearch),
list -> mList.setValue(list)
);
}
}
UPDATE
I resolved this using transformation a package from lifecycle library...
enter link description here
public class ListItemViewModel extends ViewModel {
private final MutableLiveData<String> mQuery = new MutableLiveData<>();
private MeliRepository meliRepository;
private LiveData<ItemList> mList = Transformations.switchMap(mQuery, text -> {
return meliRepository.getItemsByQuery(text);
});
public ListItemViewModel(MeliRepository repository){
meliRepository = repository;
}
public LiveData<ItemList> getItemList(String query){
return mList;
}
}
#John this is my solution. I'm using lifecycle library and the solution was easier than I thought. Thx!
I'm more familiar with doing this in Kotlin but you should be able to translate this to Java easily enough (or perhaps now is a good time to start using Kotlin :) )....adapting similar pattern I have here I believe you'd do something like:
val query: MutableLiveData<String> = MutableLiveData()
val mList = MediatorLiveData<List<ItemList>>().apply {
this.addSource(query) {
this.value = meliRepository.getItemsByQuery(query)
}
}
fun setQuery(q: String) {
query.value = q
}
I'm using this pattern in following https://github.com/joreilly/galway-bus-android/blob/master/app/src/main/java/com/surrus/galwaybus/ui/viewmodel/BusStopsViewModel.kt

Android ROOM - LiveData not triggered when using custom insert query

I'm trying to store values of some variable that my application regulary obtains from API. I whant to add new row to the database table only when variable changes its value to be able to show user some kind of "history of changes". I'm using ROOM for storing data.
I've created an entity:
#Entity(tableName = "balance_history",
indices = {#Index("received_at")})
public class BalanceResponse {
//region getters & setters
...
//endregion
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id")
private long mId;
#ColumnInfo(name = "money")
private double mMoney;
#ColumnInfo(name = "received_at")
private DateTime mReceivedAt;
}
Dao:
#Dao
public abstract class DatabaseDao {
#Query("SELECT * FROM balance_history ORDER BY received_at DESC LIMIT 1")
public abstract LiveData<BalanceResponse> selectLatestBalanceResponse();
public void insertNewBalanceResponse(BalanceResponse balanceResponse) {
String sqlRequest = "INSERT INTO balance_history(money, received_at) " +
"SELECT ?, ? " +
"WHERE NOT EXISTS(SELECT 1 FROM (SELECT * FROM balance_history ORDER BY received_at DESC LIMIT 1) WHERE money = ?);";
SupportSQLiteDatabase database = DatabaseStorage.getInstance().getAppDatabase().getOpenHelper().getWritableDatabase();
database.execSQL(sqlRequest,
new Object[]{balanceResponse.getMoney(), balanceResponse.getReceivedAt().getMillis(), balanceResponse.getMoney()});
}
}
Database object:
#Database(entities = {BalanceResponse.class}, version = 1)
#TypeConverters(DateTimeConverter.class)
public abstract class AppDatabase
extends RoomDatabase {
public abstract DatabaseDao getDatabseDao();
}
Singleton for storing single database object:
public class DatabaseStorage {
//region singleton
private static final DatabaseStorage ourInstance = new DatabaseStorage();
public static DatabaseStorage getInstance() {
return ourInstance;
}
//endregion
#NonNull
public AppDatabase getAppDatabase() {
return mAppDatabase;
}
#NonNull
private AppDatabase mAppDatabase;
private DatabaseStorage() {
mAppDatabase =
Room.databaseBuilder(MyApp.getAppContext(), AppDatabase.class, "app-database")
.build();
}
}
And viewmodel that I instantiate in my Activity's onCreate():
public class BalanceView implements Observer<BalanceResponse> {
private LiveData<BalanceResponse> mLatestBalanceResponse;
public BalanceView(LifecycleOwner lifecycleOwner){
mLatestBalanceResponse = DatabaseStorage.getInstance().getAppDatabase().getDatabseDao()
.selectLatestBalanceResponse();
mLatestBalanceResponse.observe(lifecycleOwner, this);
//finding views here
}
#Override
public void onChanged(#Nullable BalanceResponse balanceResponse) {
//displaying changes here
}
}
I've expected triggering of BalanceView.onChanges() method each time when method DatabaseDao.insertNewBalanceResponse() inserts a row.
Actually BalanceView.onChanges() method never gets fired. Why is that so? How can I accomplish this?
p.s. However, If I replace method DatabaseDao.insertNewBalanceResponse() with original:
#Insert
public abstract Long insertBalanceResponse(BalanceResponse balanceResponse);
Everithing works fine and method onChange() gets invoked. But this kind of insert statement doesn't fit my needs.
I have the same issue and here I got a hint to solve this issue.
#Dao
interface RawDao {
#RawQuery
User getUserViaQuery(SupportSQLiteQuery query);
}
SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT * FROM User WHERE id = ? LIMIT 1",
new Object[]{userId});
User user2 = rawDao.getUserViaQuery(query);
For More details, check https://developer.android.com/reference/android/arch/persistence/room/RawQuery.

How write Create and Update Query in Room Library?

I go through SQL query other solution. i am not able to find suitable solutions for my problem.
In My project, i have the insert data into the table. I have to follow some steps
Step1:- I have to check data through a primary key that the data is available or not.
Step 2: if data is available then I have to update that data and return response code. if not I have to go step 3
Step 3: if data is not in the table then insert data into it and return code.
I am using Room Library. i am confused how to write in #Dao to perform that task.
Thanks in advance
Android Architecture Components introduced Android Room Persistence Library which is best for sqlite android database handling. Entity in Room Persistence represents a database table and Dao is where we define database interactions. Example
#Entity
public class Trail {
public #PrimaryKey String id;
public String name;
public double kilometers;
public int difficulty;
}
possible Dao for this table will be
#Dao
public interface TrailDao {
#Insert(onConflict = IGNORE)
void insertTrail(Trail trail);
#Query("SELECT * FROM Trail")
List<Trail> findAllTrails();
#Update(onConflict = REPLACE)
void updateTrail(Trail trail);
#Query("DELETE FROM Trail")
void deleteAll();
}
Further you need to provide RoomDatabase implementation, Example
#Database(entities = {Trail.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract TrailDao trailDao();
public static AppDatabase getInMemoryDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE =
Room.inMemoryDatabaseBuilder(context.getApplicationContext(), AppDatabase.class)
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
public static void destroyInstance() {
INSTANCE = null;
}
}
Use It like
AppDatabase. getInMemoryDatabase(context).trailDao().findAllTrails();

Categories

Resources