I ran across a problem where I am not really sure how to solve it. The project I am working on currently has a model which partly consists of backend stored data and data from the local database.
So what I am trying to Archive is something like that:
Article : [Bunch of Information] & [boolean Subscribed]
The subscribed field is device bound and should not reflect any data on the backend. My question is if it is possible to implement in Room some kind of createIfNotExit() Method that handles the following cases:
Article not present locally: store a copy and set Subscribed to
false
Article present: update all the Information and Keep the
Subscribe-Flag untouched
My idea is to split the model into a separate Subscription-Model holding a reference to the Article. This way I could implement it simply via #Update(OnConfict=Update) etc...
Is there a way to implement a simple #Query method in the DAO that performs what I want?
Sorry if this is a really basic question but I couldn't find any material about best practices handling this case.
Thank you in advance!
For example, your entity is:
#Entity(tableName = "articles")
public final class Article {
#PrimaryKey
public long serverId;
public String title;
public String url;
public boolean isSubscribed;
}
You may write this method in DAO:
#Query("INSERT OR REPLACE INTO articles (serverId, title, url, isSubscribed) VALUES (:id, :title, :url,
COALESCE((SELECT isSubscribed FROM articles WHERE id = :id), 0));")
void insertOrUpdateArticle(long id, String title, String url);
Another option - write this logic in your repository and use two simple operations: select and update
Related
I am learning Android Architecture Components.
For exemple, and be more easier to understand, If i want to build a TO DO LIST app, my item creation DAO should be
#Dao
public interface ItemDao {
#Insert
long insertItem(Item item);
}
and my viewModel could be use this DAO to insert an item in my TODO list.
But, in architecture component, it is recommanded to NOT manipulate the database by the viewmodel but by the repository.
So, the code should be like that
public class ItemDataRepository {
private final ItemDao itemDao;
public ItemDataRepository(ItemDao itemDao) { this.itemDao = itemDao; }
// --- CREATE ---
public void createItem(Item item){ itemDao.insertItem(item); }
It seems redundant when we cannot understand why.
My question is : why?
I use the Repository for a couple of reasons:
Separation of concern I let the repo be responsible for downloading and storing all the data. That way the ViewModel doesn't have to know any specifics about where the data is coming from, e.g. if it's from an API or a cache. It also makes it easier to write Unit tests for the ViewModel, since all the database and API logic should already be tested in Unit tests for the Repository.
Reusability Lets say you fetch the data from an API and store in a database. If you put the code in the ViewModel and then want to perform the same actions from another place in the app, you need to copy paste the code. By having it in a Repository you can easily share the implementation.
Share data If you have multiple screens that show parts of the same data set, having one Repository passed around between the screens make it easy to share the data. No more trying to pass large amount of data in Bundle and Intent.
Lifecycle Let's say you download data from an API to show in your view. If you fetch the data in the ViewModel you will have to re-download it when the screen is closed and re-opened, since the ViewModel is discarded. However if you store it in the Repository it can use the Application lifecycle, so when you revisit the screen again, the data is already in the cache.
I am developing an android chat application, I have several types of messages inherited from a single abstract class. I want to get a list of different types of chat messages. I think I need an ORM with inheritance support with a SINGLE_TABLE strategy. Is there an ORM for Android with support for this functionality? Or, perhaps, you will advise how to solve this problem using the ORM without SINGLE_TABLE support?
Examples:
public abstract class AbstractMessage implements MessageListContent, Serializable, Comparable<AbstractMessage> {
public enum Status {
DELIVERED,
SENDING_AND_VALIDATION,
NOT_SENDED,
INVALIDATED
}
private SupportedMessageListContentType supportedType = SupportedMessageListContentType.UNDEFINED;
private boolean iSay;
private long timestamp;
private Status status = Status.SENDING_AND_VALIDATION;
private String transactionId;
private String companionId;
// getters and setters
//...
}
public class BasicMessage extends AbstractMessage {
private String text;
private transient Spanned htmlText;
// getters and setters
//...
}
public class TransferMessage extends AbstractMessage {
private BigDecimal amount;
// getters and setters
//...
}
I don't know if you know a lot about ORM's in android, but, two of the most famous ORM's for Android are Room and Realm. And these two could achieve what you want. But, don't take my word on realm, as I am only going to state what my friend told me about realm.
For starters, Room runs in SQLite and Realm in NoSQL. Now, I assume that you know greatly about inheritance and polymorphism, this concept could also be applied to SQL. This is, taking in account that you choose SQLite. Now for realm, it is a different story tho. My friend told me that the polymorphism of your models answers to the polymorphism of the database. Though, I highly doubt that, but I don't like realm, so don't take my word for it.
For choosing your database, I will be frank to tell you to choose SQLite and to help you decide, here is a simple site that currated reasons on which is better, SQL or NoSQL: http://www.nosql-vs-sql.com/
Still new to Room and while most of the tutorials I've found are related to simple table and CRUD operations I am stuck on evolving this.
Let's take this sample structures.
Users entity
#Entity(tableName = "users")
public class UsersEntity{
#PrimaryKey(autoGenerate = true)
private long id;
#NonNull
private String name;
#NonNull
private long roleId;
}
Roles entity
#Entity(tableName = "roles")
public class RolesEntity{
#PrimaryKey(autoGenerate = true)
private long id;
#NonNull
private String name;
}
First question: Should Entity objects be extended to also replace POJO? Or have Entities and POJO as separate classes?
Extending from the Room setup, the way I would see User POJO is:
public class User{
private long id;
private Role role;
}
Basically this setup should work both if the User would come as a json response from a web service or entered by the user in the app's input fields.
However, this raises the second question: how to insert and retrieve user info?.
Inserting seems possible as there could be something like
userDAO.insertRole(userId)
But how can I get the Role object of User by using Room and the userDAO?
I find inappropriate to do something like:
user = userDao.getUser(userId)
user.setRole(roleDao.getRole(user.getRoleId)
Third question: it seems to be a good practice to have the table columns with _ (eg. role_id) but in java roleId is recommended as class property. If the result of a #Query for instance select role_id from... and the the POJO with roleId field, will fail so the query needs to be select role_id as roleId... to get it work. Is it a good practice to use camel case in table/column names in sqlite?
What you intend as POJO, probably can be seen as a kind of a view model. In general it is a bad idea to unify/link entities and pojos because you are just making a long wider visibilty/scope for the entity, where it is not necessary and can lead to potential problems.
Say you have some client which requires some different visualization of the data, for instance imagine you have a website which exposes a vehicle data and you have implemented everything using metric system, so for distance you have km, for speed km/h and so on. Now your company gains a huge client from UK, and they want you to provide them the data in imperial format. What to do now? Probably implement a deserilization/conversion process which takes the values and converts them according to the context of the user (whether they are using metric or imperial system). What could possibly go wrong if your entity and view model objects are basically the same? Really bad stuff. You have really tight coupling of things, you should implement different getters for serialization for client, for db..it can become a mess.
Instead if you separate the two, you will have your entity which takes care of working with the database, which is standard procedure with small coefficient of variability, and on the other side you will have the view model which is very likely to require frequent modification, after all it is expected, since it is the interface to the final user.
When using Firebase to store and retrieve objects (POJOs) created by the user (for example: posts or comments), it becomes necessary to pass these objects around the application. But what is the suggested way to keep track of the associated DatabaseReference, location or unique key in the database for this object?
Example scenario
A simple to do list app allows the user to freely add, edit and remove items in their list. So when the user creates an item, something similar to the below would happen:
private Item storeItem(String title) {
String key = mDatabase.child("items").push().getKey(); // Where do we keep this key?
Item item = new Item(title);
mDatabase.child("items").child(key).setValue(item);
return item;
}
Where Item is this Java object:
public class Item {
private String title;
private String description;
public Item() {}
public Item(String title) {
this.title = title;
}
// ...
}
Behind the scenes, this item is added to a RecyclerView, either by inserting the returned Item to the adapter or when a ChildEventListener attached to the "items" reference is fired.
The user then wishes to rename this new item or add text to the description field, so tapping on it in the RecyclerView starts a separate Activity which receives the passed Item and uses getters/setters to make changes.
Now, we'll need to save these changes to the database, which we can do by calling setValue() again, as above. However, we didn't store the key variable from storeItem() so we don't actually know where this item is currently stored in the database.
So, where can we keep track of the created item's key for later use to save changes back to the database?
Possible solutions
There are a number of different paths we could take here, but I'm looking for some guidance on the suggested method, as the Firebase documentation doesn't mention this hurdle. I've outlined some examples that I can think of:
Store the key inside the object. We could add another field to the Item object to store the database key. So within the previous storeItem() method, the key variable is added to the Item constructor and stored in the database as a field.
Create a wrapper object. We could wrap the Item object in a container that has methods such as getItem() and getKey() or getDatabaseReference() and then pass this around the app instead of the Item itself.
Use the DataSnapshot instead. Once the item is created, wait until an attached listener receives it, then use and pass around the retrieved DataSnapshot, which has methods for getKey() and getRef().
Retrieve the object every time it is needed. Instead of passing Item around the app, we could retrieve it from the database every time it is needed, by using the key or DatabaseReference.
Wrapping up
Looking back on this huge question, it seems I might have overcomplicated it a little, but I wanted to be thorough in my explanation. I'm also hoping that it's not purely opinion-based and there currently is some standard way to achieve this.
So I guess my question is: is there a standard method to handle and make changes to Java objects stored in Firebase?
Most developers I see struggling with this end up storing the key inside the Java objects too. To prevent it being duplicated in the JSON, you can annotate it in the Java class:
public class Item {
private String title;
private String description;
#Exclude
public String key;
public Item() {}
public Item(String title) {
this.title = title;
}
// ...
}
See: Is there a way to store Key in class which I cast from Firebase object?
My personal preference in such cases is to keep the DataSnapshot around. The main disadvantage I see in that is that the information on the object-type of the snapshot is spreading out over my code since this exists in multiple places:
snapshot.getValue(Item.class);
I've been lobbying to generify the DataSnapshot class so that it'd become DataSnapshot<Item>, which would solve that problem. I think that is currently being considered in the Firestore SDK for JavaScript/TypeScript.
But lacking such a solution for the Android SDK for the Realtime Database, you're probably better off with the first approach: storing the key inside the Java objects.
We are building a project using couchbase. On Android, I use couchbase lite. Usually, I've been working with relational databases and because I am new to couchbase I am having trouble finding the "correct" architecture. I do understand the core concepts I think, but all the samples and guides seem to stick to some kind of easy setup where they access the database right in the Activities.
I am more used to having some database abstraction where the business logic only get's to see POJO DTO's that are delivered through a database interface or some DAO or something. So I've now annotated my model classes and started writing a simple OR mapper, but with different types of data, foreign keys etc. this is getting quite time consuming quite fast.
Am I completely missing the point here somehow? I can't imagine everyone doing it this way? I everyone writing methods that convert Documents to POJO model classes for each class seperately? Or using a json parser to do that (But that won't work for foreign keys if I wan't to load them too, does it)?
Sorry for the load of questions, but I feel I am missing something obvious here. Thanks!
Will try answering your questions:
Am I completely missing the point here somehow?
No. You can treat noSQL CB as a persistent distributed object cache. So its not RDBMS. However, DAO pattern perfectly fits into this model...since you are dealing with DTOs/ValueObjects/POJOs on DAO level and on noSQL level.
I can't imagine everyone doing it this way?
I suggest write one universal Couchbase manager class that can persist/retrieve a POJO. Then you can re-use it in your DAOs.
Everyone writing methods that convert Documents to POJO model classes
for each class separately? Or using a json parser to do that (But that
won't work for foreign keys if I wan't to load them too, does it)?
You can have one common code in your Couchbase manager class that does conversion from/to json to POJO. So you work with only POJOs and don't see any json in your application code (outside of Couchbase manager class)
Here is an example of such class:
public class CouchbaseManager<K, V>
{
private final Class<V> valueTypeParameterClass;
#Inject
private CouchbaseClient cbClient;
#Inject
private Gson gson;
public CouchbaseManager(final Class<V> valueClass)
{
this.valueTypeParameterClass = valueClass;
}
public V get(K key)
{
V res = null;
String jsonValue = null;
if (key != null)
{
jsonValue = (String) cbClient.get(key);
if (jsonValue != null)
{
res = gson.fromJson(jsonValue, valueTypeParameterClass);
}
}
return res;
}
public void put(K key, V value)
{
int ttl = 0;
cbClient.set(key, ttl, gson.toJson(value, valueTypeParameterClass));
}
}
Then in your DAO code you create instance of CouchbaseManager for each type:
CouchbaseManager<String,Customer> cbmCustomer = new CouchbaseManager<String,Customer>(Customer.class);
CouchbaseManager<String,Account> cbmAccount = new CouchbaseManager<String,Account>(Account.class);
// and so on for other POJOs you have.
// then get/put operations look simple
Customer cust = cbmCustomer.get("cust-1234");
cust.setName("New Name"); // mutate value
// store changes
cbmCustomer.put(cust.getId(), cust);
Now regarding "foreign keys". Remember its not RDBMS so its up to your code to have notion of a "foreign key". For example a Customer class can have an id of an account:
Customer cust = cbmCustomer.get("cust-1234");
String accId = cust.getAccountId();
//You can load account
Account acc = cbmAccount.get(accId);
So as you can see you are doing it all yourself. I wish it was JPA or JDO implementation/provider for Couchbase (like DataNucleus or Hibernate)
You should really start with your POJO/Document design to try to split your POJO entities into "chunks" of data to get a right balance between coarse vs fine grained POJOs.
Also see this discussion on key/document design considerations.