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.
Related
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.
I'm working on an Android application using Couchbase lite.
Should I have my classes extending com.couchbase.lite.Document ?
Pros: DAO is integrated in class.
Cons: - every object is linked to a document, if we want a new object, we must create a new document in couchbase? - anything else?
For example:
public class UserProfile extends Document {
public UserProfile (Database database, String documentId);
public Map<String, Object> getProperties();
public boolean isModified();
public boolean update() throws CouchbaseLiteException {
if (isModified()) {
super.putProperties(getProperties());
return true;
}
else
return false;
}
}
I would not recommend extending Document. Instead, either just use Maps, or use something like the Jackson JSON library to create POJOs. I usually create a simple helper class to wrap the database operations (including replication, if you're using that).
Off the top of my head, I wouldn't do it because subclassing doesn't fit well with some of the ways you retrieve documents, documents are somewhat heavy-weight objects, and the preferred way to update takes into account the possibility of conflicts, which would be much more difficult. (See this blog post for a discussion of that last point.)
I've never tried to work around these issues in a subclassing approach, but it seems pretty certain to be more pain than it's worth.
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
I created a simple Web Server which use Hibernate to store entities on a MySQL database. Also, as you can expect, it shares some libraries with the clients in the <...>.shared> package to access various resources. Among them, there are the POJO classes, annotated with both Hibernate and Jackson annotations. This is an example of a POJO class.
#JsonInclude(Include.NON_NULL)
#Entity
#Table(name = "user", uniqueConstraints =
{ #UniqueConstraint(columnNames = "email"),
#UniqueConstraint(columnNames = "nick") })
public class User implements java.io.Serializable, RecognizedServerEntities
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer userId;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "threadAuthor",
orphanRemoval = false)
#Cascade({ CascadeType.SAVE_UPDATE })
private Set<Thread> userThreads = new HashSet<Thread>(0);
}
Now, I'm trying to create an android app that simply use these shared classes: the app doesn't need to know anything about Hibernate, Javax.persistence and maybe even Jackson annotations.
However, when I create a new Android project, it requires a lot of libraries.
74K hibernate-commons-annotations-4.0.5.Final.jar
5,1M hibernate-core-4.3.6.Final.jar
38K jackson-annotations-2.4.0.jar
221K jackson-core-2.4.3.jar
1,1M jackson-databind-2.4.3.jar
180K javax.persistence_2.1.0.v201304241213.jar
714K org.restlet.jar
55K shared.jar
Problem is that they greatly magnify the app size and slow down the development process, because I have to enable Multidex support.
So, how can I solve?
I can think of some solutions:
Change the shared classes in some way to not expose the annotations. Is it a viable solution? How can I efficiently do this?
Use ProGuard. I don't know if it's really a solution because I don't know anything about this tool, but for what I've read it could help to delete classes that aren't used at all in the project.
EDIT: I partially worked around the problem extracting only required casses from hibernate-core-4.3.6.Final.jar, which is the greatest libraries. Still looking for most elegant solutions.
If you think of your application as having layers, you are currently using the same classes across the data access and view layers.
Your database isn't interested in the Jackson annotations, and your Android client isn't interested in the Hibernate annotations.
Two different approaches that I have seen for this are:
- have a parallel 'view' layer representation of your objects that has the Jackson annotations which your application will populate by mapping from your Hibernate annotated data access layer
OR
- have your client application not share the classes of the server application. It parses the JSON and maps the structures into your own client specific model.
These approaches are equivalent, but just vary by where the mapping takes place.
To serve a backend for an Android App i am using Google App Engine together with Objectify (4.0.3b).
On the backend I have a simple User Entity, which has a list of Users (friends) as relationship.
#Entity
public class User {
#Id
private String email;
#Load
private List<Ref<User>> friends = new ArrayList<Ref<User>>();
private User() {
}
public List<User> getFriends() {
ArrayList<User> friendList = new ArrayList<User>();
for (Ref<User> ref : this.friends) {
friendList.add(ref.get());
}
return friendList;
}
public void setFriends(List<User> friends) {
List<Ref<User>> refs = new ArrayList<Ref<User>>();
for (User user : friends) {
refs.add(Ref.create(user));
}
this.friends = refs;
}
}
Now when I have following Users stored in the Database for instance : user1 and user2:
user1 has user2 in his friend list and vice versa
When trying to fetch a User object (that has the above cycle reference) from my Endpoint, the Android client throws the following exception:
com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]...
In this post Infinite Recursion with Jackson JSON and Hibernate JPA issue they recommend to use #JsonIgnore on the field or the getter/setter method.
But on the client side i need to access the friends list by these getter/setter methods (from the generated client library object), so this doesn't help me.
Another tip is to use #JsonManagedReference and #JsonBackReference, which in my case can't be applied since ManagedReference and BackReference would pointto the same friends field.
What I think could solve my Problem is the #JsonIdentityInfo Annotation which is available since Jackson 2.0.
My problem now is that I don't know how I can use this Annotation with Google App Engine.
GAE uses jackson-core-asl-1.9.11, which unfortunately does not have the #JsonIdentityInfo, as it obviously is depending on a version below 2.0.
Does anybody know, how I can use the latest Jackson Version (2.4) in Google App Engine to use the #JsonIdentityInfo feature?
Or is there a better approach for my problem?
The best approach would be to define a DTO class to use as a return parameter for your endpoint and use that to flatten the JSON response (meaning not sending the infinite loop of friend's friends).