I'm using a room database for saving some apps details in a table. And getting live data.
But I'm unable to use conditions.
#Query("SELECT * FROM apps WHERE parent LIKE: parent")
LiveData<List<App>> getAllApp(String parent);
Here is the condition I'm trying to use in DAO. But I can't figure, where should I pass the parameter in REPOSITORY and VIEWNODEL class;
Please help me. May be my question is not that clear but please help me.
public class AppRepository {
private AppDao appDao;
private LiveData<List<App>> appLiveData;
AppRepository(Application application){
Database database = Database.getDatabase(application);
appDao = database.appDao();
appLiveData = appDao.getAllApp();
}
LiveData<List<App>> getAllApp(){
return appLiveData;
}
void insert(App app){
Database.writer.execute(() ->{
appDao.insert(app);
});
}
void delete(App app){
Database.writer.execute(() ->{
appDao.delete(app);
});
}
}
public class AppViewModel extends AndroidViewModel {
private AppRepository repository;
private LiveData<List<App>> appLiveData;
public AppViewModel(#NonNull Application application) {
super(application);
repository = new AppRepository(application);
appLiveData = repository.getAllApp();
}
public LiveData<List<App>> getAllApp( ){
return appLiveData;
}
public void insert(App app){
repository.insert(app);
}
private void delete(App app){
repository.delete(app);
}
}
Related
I want to make a Query to get all birds in a family and I'm not sure how to do it. I've done this so far but I feel like I'm missing something:
DAO:
#Query("Select * from bird WHERE family=:family")
public LiveData<List<Bird>> getBirdsFromFamily(String family);
Repository:
private LiveData<List<Bird>> allBirdsFromFamily;
private String family;
public BirdRepository(Application application){
Database database = Database.getInstance(application);
dao = database.dao();
allBirdsFromFamily = dao.getBirdsFromFamily(family);
}
public LiveData<List<Bird>> getBirdsFromFamily(String family){
return allBirdsFromFamily;
}
View model:
private LiveData<List<Bird>> allBirdsFromFamily;
private String family;
public BirdViewModel(#NonNull Application application) {
super(application);
birdRepository = new BirdRepository(application);
allBirdsFromFamily = birdRepository.getBirdsFromFamily(family);
}
public LiveData<List<Bird>> getAllBirdsFromFamily(String family)
{
return allBirdsFromFamily;
}
I have a list of families and a list of birds.
When clicking on an item of the family list, I want to open a list with all the birds inside. I can get the String out of the item but nothing appears when I call the method.
String family = intent.getStringExtra(AddEditFamilyActivity.EXTRA_FAMILY);
birdViewModel.getAllBirdsFromFamily(family).observe(this, new Observer<List<Bird>>() {
#Override
public void onChanged(#Nullable List<Bird> birds) { //everytime something changes, the adaptater is updated
//update the recycler view
adapter.submitList(birds);
}
});
As my comment is a bit long i'll make a post.
I don't have any doInBackGround for this query, how should I do it ?
Yes, when you query to the database you want to avoid using the Main Thread for 2 reasons, the first one is because you'll get a crash from Android telling you not to do it. And the second is to let your user interact with your application while it do the work of getting the data.
In this doc you can look at the samples and Codelabs and to learn how to make a call to database properly.
I resolved it with a factory - doInBackground wasn't necessary because it's only a "get" query :
DAO :
#Query("Select * from bird WHERE family=:family ORDER BY name")
public LiveData<List<Bird>> getBirdsFromFamily(String family);
Repository :
private LiveData<List<Bird>> allBirdsFromFamily;
private String family;
public BirdRepository(Application application){
Database database = Database.getInstance(application);
birdDao = database.dao();
allBirdsFromFamily = birdDao.getBirdsFromFamily(family);
}
public LiveData<List<Bird>> getAllBirdsFromFamily(String family){
return birdDao.getBirdsFromFamily(family);
}
ViewModel :
private LiveData<List<Bird>> allBirdsFromFamily;
private BirdRepository repository;
public BirdViewModel(#NonNull Application application, String family ) {
super(application);
this.application = application;
repository = new BirdRepository(application);
allBirdsFromFamily = repository.getAllBirdsFromFamily(family);
}
public LiveData<List<Bird>> getAllBirdsFromFamily(String family) { return repository.getAllBirdsFromFamily(family); }
ViewModelFactory :
private Application mApplication;
private String mfamily;
public BirdViewModelFactory(#NonNull Application application, String family) {
mApplication = application;
mfamily = family;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
return (T) new BirdViewModel(mApplication, mfamily);
}
Activity :
BirdViewModelFactory factory = new BirdViewModelFactory(this.getApplication(), family);
birdViewModel = new ViewModelProvider(this, factory).get(BirdViewModel.class);
I want to get the id auto-generated while performing insert operation on Room database. I am implementing MVVM (Model-View-ViewModel) architecture which makes use of DAO to fire queries to Room database. I have added a repository layer between viewmodel and DAO to create an AsyncTask to perform database operations. How do I get the output of insert operation (which is the inserted row's auto-generated id) to the fragment that uses the viewmodel. The layers are as follows: Fragment -> ViewModel -> Repository -> DAO
ListFragment.java
public class ListFragment extends Fragment {
private ReminderViewModel viewModel;
private int id;
...
viewModel = ViewModelProviders.of(this).get(ReminderViewModel.class);
...
id = viewModel.insert(new TodoReminder(0, description, date, time));
...
}
ReminderViewModel.java
public class ReminderViewModel extends AndroidViewModel {
private ReminderRepository repository;
public ReminderViewModel(#NonNull Application application) {
super(application);
repository = new ReminderRepository(application);
}
public int insert(TodoReminder reminder) {
repository.insert(reminder);
}
}
ReminderRepository.java
public class ReminderRepository {
private ReminderDAO reminderDAO;
public ReminderRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
reminderDAO = db.getReminderDAO();
}
public int insert(TodoReminder reminder) {
new insertAsyncTask(reminderDAO).execute(reminder);
}
private static class InsertAsyncTask extends AsyncTask<TodoReminder, Void, Integer> {
private ReminderDAO asyncTaskDAO;
insertAsyncTask(ReminderDAO dao) {
asyncTaskDAO = dao;
}
#Override
protected Integer doInBackground(final TodoReminder... reminders) {
return asyncTaskDAO.insert(reminders[0]);
}
}
}
ReminderDAO.java
#Dao
public interface ReminderDAO {
#Insert
public int insert(TodoReminder... reminders);
}
ToDoReminder.java
public class TodoReminder implements Serializable {
#PrimaryKey(autoGenerate = true)
#NonNull
private int id;
...
}
How should I get the int returned from the insert method of ReminderDAO and return it from the insert method in ReminderRepository?
You can create your database table in such a way that the id is incremented automatically. In MySQL that is done via the auto_increment keyword. In SQL Server it is done via the identity(1, 1) syntax. In Access it is the autoincrement keyword. In Oracle and PostgreSQL it is done using sequences. If you manage to do this, then you will not need to manually work on incrementing these values. If, for some reason this is out of the question, then you can create a before insert trigger which will load the maximum id and add 1 to it, storing the result in the id. Or, if even that is out of the question, then you can load the last ID, but that has a different syntax in different databases. Or, you can run a query like this:
select max(id) + 1 from yourtable;
but beware possible performance issues and concurrency problems.
I have the exact problem. This is what I did.
I created an interface
public interface Task
{
void processInsert(long id)
}
supply the interface to the insert fcn in the repository
public class Repository {
private Dao myDao;
public void insertMyObject(MyOBject object,Task myInterface ) {
new insertAysncTask(myDao,myInterface).execute(object);
}
private static class insertAsyncTask extends AysncTask<MyObject,Void,Long>{
private Dao mDao;
private Task mTask;
public insertAysncTask(Dao dao, Task task) {
this.mDao = dao;
this.mTask=task;
}
#Override
protected Long doInBackground(MyObject... myObjects) {
return mDao.insertMyObject(myObjects[0]);
}
#Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
mTask.processInsert(aLong);
}
}
}
in the DAO class fcn should have return type of Long
public interface MyDao {
#Insert
Long insertMyObject(MyObject object);
have the ViewModel implement the interface
public class MyObjectViewModel extends AndroidViewModel implements Task{
private Repository mRepository;
public MyObjectViewModel(#NonNull Application application) {
super(application);
mRepository = new Repository(application);
}
#Override
public void processInsert(Long id) {
// code for processing the id returned from the db insert
}
public void insertMyObject(MyObject object){
mRepository.insertMyObject(object,this);}
}
in the activity/fragment call the ViewModel insert
mViewModel.insertMyObject(object);
Update
If you want to return the id to the activity or fragment then have the fragment/activity implement the interface instead of the viewmodel
ListFragment extends Fragment implements Task{
.....
#Override
public void processInsert(Long id){
//process id here
}
//call the insert fcn passing in the reference to the fragment
mViewModel.insertMyObject(object,this)
}
Modify the insert fcn in the View Model to accept a reference to the interface
public void insertMyObject(MyObject object, Task myInterface){
mRepository.insertMyObject(object,myInterface);
}
with this approach, the asynctask in the repository insert fcn will hold a reference to the activity/fragment and if the activity/fragment is destroyed before the asynctask finishes this will be a problem. I think it's better to do the processing in the viewmodel if possible. Fragments/activities should only deal with UI concerns not data processing.
Alternative Method
An alternative would be to use a LiveData with an observer.
public class Repository{
private Dao myDao;
private MutableLiveData<Long> dbInsertId = new MutableLiveData<>();
public void insertMyObject(MyObject object){
insertAysnc(object)
}
private void insertAysnc(final MyObject object){
new Thread(new Runnable() {
#Override
public void run() {
Long id=myDao.insertMyObject(object); //call Dao insert fcn
dbInsertId.postValue(id); //set the value of the livedata to the valued returned from the DB insert fcn
}
}).start();
}
public LiveData<Long> getDbInsertedId(){
return dbInsertId;
}
}
In the ViewModel define this fcn
public LiveData<Long> getDbInsertedId(){
return mRepository.getDbInsertedId();// call the repository getId fcn
}
in the onCreate of Activity/Fragment setup an observer on the LiveData
mViewModel= ViewModelProviders.of(this).get(MyViewModel.class);
mViewModel.getDbInsertedId().observe(this, new Observer<Long>() {
#Override
public void onChanged(Long aLong) {
// do something with the long value returned from the database
}
});
I created a room database following this guide from code labs It makes use of a repository to:
A Repository manages query threads and allows you to use multiple backends. In the most common example, the Repository implements the logic for deciding whether to fetch data from a network or use results cached in a local database.
I followed the guide and i'm now able to create the entity's & retrieve the data. I even went further and created another whole entity outside the scope of the guide.
However I can't find many resources that use this MVVM(?) style so am struggling as to really under stand the repository. For now I want to update a field. Just one, as if I am able to manage that the rest should be similar.
I want to update a field called dartshit and I have the dao method created for this:
#Query("UPDATE AtcUserStats SET dartsHit = :amount WHERE userName = :userName")
void UpdateHitAmount(int amount, String userName);
I have one repository which I assumed I use for all entities:
public class UsersRepository {
private UsersDao mUsersDao;
private AtcDao mAtcDao;
private LiveData<List<Users>> mAllUsers;
private LiveData<List<AtcUserStats>> mAllAtc;
private AtcUserStats mAtcUser;
UsersRepository(Application application) {
AppDatabase db = AppDatabase.getDatabase(application);
mUsersDao = db.usersDao();
mAtcDao = db.atcDao();
mAllUsers = mUsersDao.fetchAllUsers();
mAllAtc = mAtcDao.getAllAtcStats();
}
LiveData<List<Users>> getAllUsers() {
return mAllUsers;
}
LiveData<List<AtcUserStats>> getAllAtcStats() {
return mAllAtc;
}
LiveData<AtcUserStats> getAtcUser(String username) {
return mAtcDao.findByName(username);
}
public void insert (Users user) {
new insertAsyncTask(mUsersDao).execute(user);
}
public void insertAtc (AtcUserStats atc) {
new insertAsyncAtcTask(mAtcDao).execute(atc);
}
private static class insertAsyncTask extends AsyncTask<Users, Void, Void> {
private UsersDao mAsyncTaskDao;
insertAsyncTask(UsersDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final Users... params) {
mAsyncTaskDao.insertNewUser(params[0]);
return null;
}
}
private static class insertAsyncAtcTask extends AsyncTask<AtcUserStats, Void, Void> {
private AtcDao mAsyncTaskDao;
insertAsyncAtcTask(AtcDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final AtcUserStats... params) {
mAsyncTaskDao.insertNewAtcUser(params[0]);
return null;
}
}
}
My question is how do I create a AsyncTask for the update query I am trying to run in this repository?
Here is what I have so far by broadly copying the insert repository methods:
private class updateHitAsyncTask {
private AtcDao mAsyncTaskDao;
public updateHitAsyncTask(AtcDao mAtcDao) {
mAsyncTaskDao = mAtcDao;
}
protected Void doInBackground(int amount, String name) {
mAsyncTaskDao.UpdateHitAmount(amount, name);
return null;
}
}
Which is incorrect is that I'm getting a llegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. error. But i thought this AsyncTask is suppose to take care of this?
Here is my update method in my view model, which is reporting 0 errors:
void updateHitAmount (int amount, String name) {
mRepository.updateAtcHits(amount, name);
}
and here is the UI code where im actually trying to tie all these together, I suspect there must be a better way that using onChanged for simply updating a field but again I am struggling to come across any advice on google with the repository approach:
private void callOnChanged() {
mAtcViewModel = ViewModelProviders.of(this).get(AtcViewModel.class);
mAtcViewModel.getAllUsers().observe(this, new Observer<List<AtcUserStats>>() {
#Override
public void onChanged(#Nullable final List<AtcUserStats> atc) {
// Update the cached copy of the users in the adapter.
for (int i = 0; i < atc.size(); i++) {
if (atc.get(i).getUserName().equals(mUser)) {
mAtcViewModel.updateHitAmount(55, mUser);
//atc.get(i).setDartsHit(55);
Log.d("id", String.valueOf(userSelected.getId()));
}
}
}
});
How can I update fields using this approach on the background thread?
Figured it out due to this answer here. It was mostly because of my lack of understanding of AsyncTask. Essentially I needed to create an object and pass the data that way and then execute in the background:
private static class MyTaskParams {
int amount;
String name;
MyTaskParams(int amount, String name) {
this.amount = amount;
this.name = name;
}
}
public void updateAtcHits (int amount, String name) {
MyTaskParams params = new MyTaskParams(amount,name);
new updateHitAsyncTask(mAtcDao).execute(params);
}
private class updateHitAsyncTask extends AsyncTask<MyTaskParams,Void,Void>{
private AtcDao mAsyncTaskDao;
public updateHitAsyncTask(AtcDao mAtcDao) {
mAsyncTaskDao = mAtcDao;
}
#Override
protected Void doInBackground(MyTaskParams... myTaskParams) {
int amount =myTaskParams[0].amount;
String name = myTaskParams[0].name;
mAsyncTaskDao.UpdateHitAmount(amount, name);
return null;
}
}
I've been using the codelabs for my Room Database Persistence. Now I am trying to get the latest rowID after inserting data into my room database. However, I am stuck in my repository trying to return the rowID from my AsyncTask.
LogEntity.java
#Entity
public class LogEntity {
#PrimaryKey(autoGenerate = true)
private int id;
LogDao.java
public interface LogDao {
#Insert
long insert(LogEntity logEntity);
LogDatabase.java
#Database(entities = LogEntity.class, version = 1)
public abstract class LogDatabase extends RoomDatabase {
private static LogDatabase instance;
public abstract LogDao logDao();
public static synchronized LogDatabase getInstance(Context context){
if (instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(),
LogDatabase.class, "log_database").
fallbackToDestructiveMigration().build();
}
return instance;
}
}
LogRepository.java
public long insertLogs(LogEntity logEntity) {
new InsertLogAsyncTask(logDao).execute(logEntity);
return **
}
private static class InsertLogAsyncTask extends AsyncTask<LogEntity, Void, Long>{
private LogDao logDao;
private InsertLogAsyncTask(LogDao logDao){
this.logDao = logDao;
}
#Override
protected Long doInBackground(LogEntity... logEntities) {
logDao.insert(logEntities[0]);
return logDao.insert(logEntities[0]);
}
}
I put two asterisks because I'm not sure what to do here in order to get the insert RowID and whether my AsyncTask is completely correct.
LogViewModel.java
public long insertLog(LogEntity logEntity){
return repository.insertLogs(logEntity);
}
MainActivity.java
long id = logViewModel.insertLog(logEntity);
I want to be able to use this final id variable for future use.
You are on the right track but not quite there.
You should declare the AsyncTask class as an inner class of the ViewModel and not the DB.
In the ViewModel add an ID variable and in the AsyncTask add the onPostExecute override to handle the execution result.
LogViewModel.java
long mLastInsertedID;
private static class InsertLogAsyncTask extends AsyncTask<LogEntity, Void, Long>{
private LogDao logDao;
private InsertLogAsyncTask(LogDao logDao){
this.logDao = logDao;
}
#Override
protected Long doInBackground(LogEntity... logEntities) {
//you are now off the UI thread
logDao.insert(logEntities[0]);
return logDao.insert(logEntities[0]);
}
#Override
protected void onPostExecute(Long result) {
//Do whatever you like with the result as you are back on the UI thread
mLastInsertedID = result;
}
}
Could you guys please help me find where I made a mistake ?
I switched from SimpleBeanEditorDriver to RequestFactoryEditorDriver and my code no longer saves full graph even though with() method is called. But it correctly loads full graph in the constructor.
Could it be caused by circular reference between OrganizationProxy and PersonProxy ? I don't know what else to think :( It worked with SimpleBeanEditorDriver though.
Below is my client code. Let me know if you want me to add sources of proxies to this question (or you can see them here).
public class NewOrderView extends Composite
{
interface Binder extends UiBinder<Widget, NewOrderView> {}
private static Binder uiBinder = GWT.create(Binder.class);
interface Driver extends RequestFactoryEditorDriver<OrganizationProxy, OrganizationEditor> {}
Driver driver = GWT.create(Driver.class);
#UiField
Button save;
#UiField
OrganizationEditor orgEditor;
AdminRequestFactory requestFactory;
AdminRequestFactory.OrderRequestContext requestContext;
OrganizationProxy organization;
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
requestFactory = createFactory();
requestContext = requestFactory.contextOrder();
driver.initialize(requestFactory, orgEditor);
String[] paths = driver.getPaths();
createFactory().contextOrder().findOrganizationById(1).with(paths).fire(new Receiver<OrganizationProxy>()
{
#Override
public void onSuccess(OrganizationProxy response)
{
if (response == null)
{
organization = requestContext.create(OrganizationProxy.class);
organization.setContactPerson(requestContext.create(PersonProxy.class));
} else
organization = requestContext.edit(response);
driver.edit(organization, requestContext);
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
private static AdminRequestFactory createFactory()
{
AdminRequestFactory factory = GWT.create(AdminRequestFactory.class);
factory.initialize(new SimpleEventBus());
return factory;
}
#UiHandler("save")
void buttonClick(ClickEvent e)
{
e.stopPropagation();
save.setEnabled(false);
try
{
AdminRequestFactory.OrderRequestContext ctx = (AdminRequestFactory.OrderRequestContext) driver.flush();
if (!driver.hasErrors())
{
// Link to each other
PersonProxy contactPerson = organization.getContactPerson();
contactPerson.setOrganization(organization);
String[] paths = driver.getPaths();
ctx.saveOrganization(organization).with(paths).fire(new Receiver<Void>()
{
#Override
public void onSuccess(Void arg0)
{
createConfirmationDialogBox("Saved!").center();
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
} finally
{
save.setEnabled(true);
}
}
}
with() is only used for retrieval of information, so your with() use with a void return type is useless (but harmless).
Whether a full graph is persisted is entirely up to your server-side code, which is intimately bound to your persistence API (JPA, JDO, etc.)
First, check that the Organization object you receive in your save() method on the server-side is correctly populated. If it's not the case, check your Locators (and/or static findXxx methods) ; otherwise, check your save() method's code.
Judging from the code above, I can't see a reason why it wouldn't work.
It took me some time to realize that the problem was the composite id of Person entity.
Below is the code snippet of PojoLocator that is used by my proxy entities.
public class PojoLocator extends Locator<DatastoreObject, Long>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, Long id)
{
}
#Override
public Long getId(DatastoreObject domainObject)
{
}
}
In order to fetch child entity from DataStore you need to have id of a parent class. In order to achieve that I switched "ID class" for Locator<> to String which represents textual form of Objectify's Key<> class.
Here is how to looks now:
public class PojoLocator extends Locator<DatastoreObject, String>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, String id)
{
Key<DatastoreObject> key = Key.create(id);
return ofy.load(key);
}
#Override
public String getId(DatastoreObject domainObject)
{
if (domainObject.getId() != null)
{
Key<DatastoreObject> key = ofy.fact().getKey(domainObject);
return key.getString();
} else
return null;
}
}
Please note that your implementation may slightly differ because I'm using Objectify4.