Android studio 3.2
public class Profile extend RealmObject {
#PrimaryKey
private long id;
}
I has List<Profile> profileList;
I this list I has 5 profiles with id = 1, 2, ...
Nice.
Now I need to delete from Realm profiles with id=1 ,id=3, id=5
So after delete the list must content only 2 profiles with id=2 and id=4
How I can do this?
P.S. The list of ids is a dynamic list. Today it has 3 ids , but tomorrow can 2 ids.
Well that's actually quite simple, considering there's direct support for it...
r.executeTransaction((realm) -> {
realm.where(Profile.class).in("id", new Long[] { 1L, 3L, 5L }).findAll().deleteAllFromRealm();
});
See https://realm.io/docs/java/latest/api/io/realm/RealmQuery.html#in-java.lang.String-java.lang.Long:A-
As per the Realm documentation, you need to search for the all possible matches
// obtain the results of a query
final RealmResults<Profile> results = realm.where(Profile.class).equalTo("profile.id", 1).where().equalTo("profile.id", 3).where().equalTo("profile.id", 5).findAll();
// All changes to data must happen in a transaction
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// remove single match
results.deleteFirstFromRealm();
results.deleteLastFromRealm();
// remove a single object
Dog dog = results.get(5);
dog.deleteFromRealm();
// Delete all matches
results.deleteAllFromRealm();
}
});
Realm doc
Related
I have LiveData for Books in ViewModel's constructor:
LiveData<List<Book>> books;
public MyViewModel(#NonNull Application application) {
super(application);
books = bookRepository.getBooks();
}
When user creates new book from UI, I want attribute book_order to be filled with incremented maximum of book_order of other books. To better describe what I want, see following preudocode:
book.book_order = max(books.book_order) + 1;
So when there are three books with book_order 1, 2, 3 respectively, new book would have this attribute set to 4.
Question is, how can I do this with LiveData in ViewModel? I tried using Transformations.map to the new LiveData, but this approach is not working at all, bookMax seems to be null.
public void insertBook(Book book) {
LiveData<Integer> bookMax = Transformations.map(books,
list -> {
int value = 0;
for(Book b: list) {
if (value < b.getBookOrder()) {
value = b.getBookOrder();
}
}
}
);
book.setBookOrder(bookMax + 1)
bookRepository.update(book);
}
Any ideas how to set incremented maximum to the new book? It can be another approach than the one described here. ViewModel was created to separate app logic from UI. However it does not seem to do that in this case, because if I want to observe value, I need to be in Activity. Also, I did not find any alternative how to do this kind of getting one value from DB. Any help appreciated.
Note that your books are livedata, thus may change its value from time to time.
Whereis your bookMax is a single value that should be calculated at the moment of insertion.
To insert you need:
get the current books list
then calculate bookMax
then actually insert.
val bookList: List<Book> = books.value // current value. may be null!
val bookMax: Int = bookList.maxBy { it.order }.order // find max order
// insert
val newBooks = arrayListOf(bookList)
newBooks.add(newBook)
books.value = newBooks // update your livedata
EDIT Here is Java code
// get current value. may be null!
List<Book> bookList = books.getValue();
// so we better handle it early
if (bookList == null) {
bookList = new ArrayList<>();
}
// calculate max order
int maxOrder = -1;
for (Book book : bookList) {
if (maxOrder < book.order) {
maxOrder = book.order;
}
}
// create new book
Book newBook = new Book();
newBook.order = maxOrder + 1;
// add book to the list
bookList.add(newBook);
// do with new list whatever you want
// for example, you can update live data (if it is a MutableLiveData)
books.setValue(bookList);
Using LiveData will not help in your scenario.
LiveData in DAO gets executed on a different thread and the new value is posted in observer code or in your case Transformation.map() callback. So you need to access book.id inside Transformation.map().
However, if you insert book in Transformation.map(), it would trigger an infinite loop since on every table entry update Transformation.map() would be called as LiveData> would change.
So, for your case:
Expose a method which exposes last book id
Insert a new entry.
Add a LiveData> to receive an update and display in UI.
Instead of taking all books for finding max book order, you should make a method in your repository that will provide you max number of book_order from db.
Something like below pseudo code :
int maxOrder = bookRepository.getMaxBookOrder();
Now, all you need to do is while inserting new book, you can use that maxOrder variable to incremental purpose.
So, your insert method will be like :
public void insertBook(Book book) {
int maxOrder = bookRepository.getMaxBookOrder();
book.setBookOrder(maxOrder + 1)
bookRepository.update(book);
}
Here, assuming that you're using ROOM for persisting database, this is the query that can help you get your maximum book_order:
SELECT MAX(book_order) FROM Book
If ROOM isn't your case then, you can do with another approach :
We first retrieve list using repository method and then find maximum from it like below pseudo :
List<Book> books = bookRepository.getBooks().getValue(); // Assuming getBooks() returns LiveData
Then find max from it and then increment it by one :
public void insertBook(Book book) {
List<Book> books = bookRepository.getBooks().getValue();
// calculating max order using loop
int maxOrder = -1;
for (Book book : books) {
if (maxOrder < book.order) {
maxOrder = book.order;
}
}
book.setBookOrder(maxOrder + 1)
bookRepository.update(book);
}
Even you can move this code of finding maximum to repository method as mentioned earlier like :
public int getMaxBookOrder() {
List<Book> books = getBooks().getValue();
// calculating max order using loop
int maxOrder = -1;
for (Book book : books) {
if (maxOrder < book.order) {
maxOrder = book.order;
}
}
return maxOrder;
}
If you really want to do it with a LiveData you can create a custom one:
class BooksLiveData(list: List<Book>) : LiveData<List<Book>>() {
val initialList: MutableList<Book> = list.toMutableList()
fun addBook(book: Book) {
with(initialList) {
// Assuming your list is ordered
add(book.copy(last().bookOrder + 1))
}
postValue(initialList)
}
}
Then you can just create it and use:
val data = bookRepository.getBooks() // Make getBooks() return BooksLiveData now
data.addBook(userCreatedBook) // This'll trigger observers as well
You can still observe this live data, since it's posting initialList when a book is added, it'll notify observers. You can change it more, for example, to return the book that's added etc.
Side note: It might be better to extend from MutableLiveData instead, since LiveData is not supposed to update its value but internally you're posting something so it might be confusing.
Approach 1:
You can use an Auto Increment field or the PrimaryKey such as an id for the book_order's functionality. You can even name it book_order if you want to. Make your Model or Entity class Like:
public class Book{
#PrimaryKey(autoGenerate = true)
private int book_order;
//other data members, constructors and methods
}
So that, the the book_order gets incremented on each Book added to the database.
Next you can have your ViewModel classs like:
public class MyViewModel extends AndroidViewModel {
private BookRepository bookRepository;
LiveData<List<Book>> books;
public MyViewModel(#NonNull Application application) {
super(application);
bookRepository = AppRepository.getInstance(application.getApplicationContext());
books = bookRepository.getBooks();
}
}
Now you can subscribe your list Activity to this ViewModel (ie. make your activity observe the ViewModel) by putting the call to following method in your activity's onCreate():
private void initViewModel() {
final Observer<List<Book>> bookObserver= new Observer<List<Book>>() {
#Override
public void onChanged(#Nullable List<Book> books) {
bookList.clear();
bookList.addAll(books);
if (mAdapter == null) {
mAdapter = new BooksAdapter(bookList, YourActivity.this);
mRecyclerView.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
}
};
mViewModel = ViewModelProviders.of(this)
.get(MyViewModel.class);
mViewModel.mBooks.observe(this, notesObserver); //mBooks is member variable in ViewModel class
}
Doing these things, you will be able to receive updates, ie. whenever a Book is added to your database by the user, the List/ Recycler view should automatically display the newly added Book.
Approach 2:
If this is not what you have wanted at all and you only want to find the latest added book's order, you can skip the third code block completely and use the following in your Dao:
#Query("SELECT COUNT(*) FROM books")
int getCount();
which gives the total number of books ie. rows in the books table, which you can then call from your repository, which in turn can be called from your ViewModel which in turn can be called from the Activity.
Approach 3:
If you want the book_order which I think is the latest number of books in the database after a new book is added, you can use Approach 1 which gives you the List of Book in the ViewModel. You can then get the number of books from the booklist count.
Important!
either way you would want to edit your insertBook() method in your editor or newBook ViewModel and make it something like:
public void insertBook(Book book) {
Book book= mLiveBook.getValue(); //declare mLiveBook as MutableLiveData<Book> in this ViewModel
//maybe put some validation here or some other logic
mRepository.insertBook(book);
}
and in your Repository corresponding insert would look like:
public void insertBook(final Book book) {
executor.execute(new Runnable() {
#Override
public void run() {
mDb.bookDao().insertBook(book);
}
});
}
and corresponding Dao method:
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertBook(Book book);
I am using firebase recyclerview to populate my data and retrieving those data from firebase realtime database. I don't want to retrieve all of my data but with a condition that is if "Available == yes " then show me the data in recyclerview. My condition is working but the problem is I have 4 data set on firebase realtime database. From them in 3 set value of available is yes .So, I supposed to get 3 list of data but I am getting all 4 set. 3 set data has actual value and 1 set taking the dummy one. How can I stop that?
FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder>(
BloodModelSchema.class,
R.layout.search_card,
UserViewHolder.class,
databaseReference
) {
#Override
protected void populateViewHolder(UserViewHolder viewHolder, BloodModelSchema model, int position) {
if(model.getAvailable().equals("Yes"))
{
viewHolder.setDetails(model.getBlood_Group(),model.getName(),
model.getArea(),model.getMobile(),
model.getEmail());
}
}
};
recyclerView.setAdapter(firebaseRecyclerAdapter);
firebaseRecyclerAdapter.notifyDataSetChanged();
If you have one databaseReference just add .limitToFirst(3)
FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder>(
BloodModelSchema.class,
R.layout.search_card,
UserViewHolder.class,
databaseReference.limitToFirst(3))
) { .....
But you can create second databaseReference2 and limit it to 4.
For different cases use two different adapters with different limitations. Good luck!
I am developing an android application where older kids can pick up younger kids and walk to school. With the application the authenticated (email and password) younger kid can choose between three adresses to get picked up. As of right now my realtime database looks like this:
I want to retrieve the different addresses and the users who picked the addresses. I am thinking I have to use recyclerview to get the data, but I am unsure on if it is possible to do with my database structure.
Using the FirebaseUI database package makes it simple to bind data from the Firebase Realtime Database to your app's UI. Specifically using FirebaseUI with indexed data is applicable for your current database structure.
For example, you'd use something similar to:
// keyQuery - the Firebase location containing the list of keys to be found in dataRef
// dataRef - the Firebase location to watch for data changes. Each key found at
// keyRef's location represents a list item.
Query keyQuery = FirebaseDatabase.getInstance().getReference("/Addresses/Street 10/users");
DatabaseReference dataRef = FirebaseDatabase.getInstance().getReference("/User");
FirebaseRecyclerOptions<User> options = new FirebaseRecyclerOptions.Builder<User>()
.setIndexedQuery(keyQuery, dataRef, User.class)
.build();
Where your User class is:
public class User {
private String username;
public User() {}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
// ...
}
You can then use the above created options variable to create a FirebaseRecyclerAdapter instance and then call startListening() on it*.
Using FirebaseUI in this way will automatically handle matching the keys under /Addresses/Street 10/users to the /User node.
* Version 2.x of FirebaseUI uses FirebaseIndexRecyclerAdapter instead and starts listening automatically so doesn't require a startListening() call. The above example is applicable for version 3.0, see FirebaseUI 3.0 upgrade guide.
Following on from my previous answer, this one should accommodate your requirement to create a list of all addresses and their associated users, which may be closer to what you're looking for.
Again you can use the FirebaseUI database package to simplify the RecyclerView creation.
You'll need to start denormalizing your data, so your data structure should also include usernames in the addresses node:
{
"Addresses" : {
"Street 10" : {
"name" : "Street 10",
"users" : {
"VAzdMWafK6cyhmJnOI4br5xiQg93" : "John"
}
}
},
"User" : {
"VAzdMWafK6cyhmJnOI4br5xiQg93" : {
"username" : "John",
"address" : "Street 10"
}
}
}
Note: you only need to add user IDs to their chosen address (and remove the node if they change selection), so don't use "VAzdMWafK6cyhmJnOI4br5xiQg93" : false for addresses the user has not selected as this could cause confusion.
Then you can use:
Query query = FirebaseDatabase.getInstance().getReference("/Addresses");
FirebaseRecyclerOptions<Address> options = new FirebaseRecyclerOptions.Builder<Address>()
.setQuery(query, Address.class)
.build();
Where Address is something like:
public class Address {
private String name;
private Map<String, String> users;
public Address() {}
public Map<String, String> getUsers() {
return this.users;
}
// ...
}
And create a FirebaseRecyclerAdapter instance from the options variable. Then when binding the viewholder in the adapter, you can access the users map to list each user that has selected this address, without the need to load the entire User object unnecessarily.
This pattern is called denormalization and is the suggested approach when using NoSQL databases (like Firebase Realtime Database). The main downside to this is data duplication: so for example, when a user changes their selected address, you'll need to change:
The address value under the user, and
the users list under the address.
Likewise, if a user is allowed to change their username, you'll need to update the username under their chosen address as well as in the user's node.
For details on dealing with this, see this answer which explains a number of methods (although the examples are in JavaScript, the premise still applies).
I have a list view in android where i have to check every time do display the List item or not
to reduce the requests what i did is saved the id in a single row like
1,2,10,
everything was working fine to search i just had to use
String[] favs = fav.split(",");
for (int index = 0; index <(favs.length); index++) {
if(favs[index]==""){}else {
wishlist.add(Integer.parseInt(favs[index].trim()));
}
if(clicklist.contains((int)temp.getId())) //like this
and to remove from db like, this
temp2.replaceAll(""+m1.getId()+",", "") // and save in the db
now issue is i have two more data field associated with id like
10|data1|data2,100|apple|dog,150|data12|data24
Question 1 is this data model ok for small db
Question 2 how to perform search and delete in new data set?
please help!
Using a db is a proper choice here, i suggest you to take a look to the recently released Room, an Android component made by Google developers to support data persistence more easily.
You should of course know the basis of sql language.
In your case you should annotate your data class with #Entity annotation:
#Entity
public class DataModel {
#PrimaryKey
private int uid;
#ColumnInfo(name = "animal")
private String animal;
#ColumnInfo(name = "fruit")
private String fruit;
// Getters and setters are ignored for brevity,
// but they're required for Room to work.
}
And then, to answer your question about CRUD operations, define a Dao:
#Dao
public interface UserDao {
#Query("SELECT * FROM DataModel")
List<DataModel> getAll();
#Insert
void insertAll(DataModel... dataModels);
#Delete
void delete(DataModel dataModel);
}
I am using Realm to store my values in local database.
My requirement is that i need to change one field status=1 based on some condition.
I have tried following method to accomplish this task. And it is working fine.
RealmResults<NotificationOrder> notificationOrders=realm
.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll();
for (NotificationOrder order:notificationOrders) {
realm.beginTransaction();
order.setStatus(1);
realm.commitTransaction();
}
Now there may be 1000 of such rows in my local db and using for loop to update single row doesn't seem proper way.
So my question :Is there any way like MYSQL Update Queries in Realm by which we can update all rows having status=0 by single statement instead of updating single row one by one ?
Thanks.
If I know right, the objects in the transaction ought to be managed, so
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<NotificationOrder> notificationOrders = realm
.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll();
for(NotificationOrder order : notificationOrders) {
order.setStatus(1);
}
}
});
Should be sufficient.
You can do, bulk update by setValue method like this:
realm.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll()
.setValue(RealmConstants.REALM_FIELD_NAME,value); // 1st parameter is field name, 2nd is value