Showing posts with label db. Show all posts

Android ORM - GreenDAO - Simplifying DB access in your app

I am currently working on a new Android app, and as usual, it requires a considerable amount of DB interaction.  I would say DB interaction isn't that difficult in Android - you can create a few simple classes to setup your schema and then DAO functions for the various domain objects that you want to map to the DB.

Simple.

Just a pain in the ass.

Coming from working on a variety of Java type ORMs (Hibernate et al), this is really just all boiler plate that is painful to write, so this time around I decided to look for an Android ORM. The first result in google was greenDAO, which claims to be being used on over 10million installed apps (AppBrain lists it as being used on Pinterest and Zynga apps), which seemed like pretty good reason to take a look.


So I started taking a look at the code on GitHub - the first thing I noticed was that it wasn't a traditional ORM that I expected, really it's more of a code generator rather than an ORM like Hibernate - I was expecting to just define some POJO entities, maybe marked up with annotations or config  that did all the magic - but what is actually going on is you have to create a "generator" project that defines your domain entities and then that produces several DAO and domain object classes that can be generated directly into your normal Android app.

Does generated code always suck?

I saw the words generator and alarm bells started to ring - I have worked with and on projects that generate code and it so often seems to have descended in to pain - often needing to slightly tweak the generated code, which then results in painful re-generation or just walking away from the generator altogether and being left with having to manually maintain generated code.

However, I had a look at the generated code and it was actually quite nice - not that different from if I had written it, and what swung it for me was the performance. I had briefly pondered building a lightweight ORM for Android more in the style of Hibernate (I love an annotation), but then I thought on the performance impact - reading annotations on POJOs at runtime - even if cached - the performance footprint was likely to be pretty big would suck for people with older/less powerful devices.


So actually, removing the pain of having to create all the DAO objects etc, whilst not having a prohibitive performance impact made this a pretty strong choice.


Let's get down to business, don't got no time to play around, what is this..

So lets have a look at how we generate the code - its actually relatively easily to programmatic-ly configure your entities for generation.





The above is an example of how you can configure two simple domain entities (which will map to a table - this is like defining a Hibernate @Entity POJO). In the above simplified, hypothetical design, we have two real domain entities "Email" and "People" - emails can be sent to multiple people, and people can recieve multiple emails.

As you can see, we define a very simple People entity. and then programatically add three columns - an ID, a name and an email address. Simple - and as you can see(or imagine) the API provides programatic constructs to configure not null, column types, etc.


All in all, so far it has been a good experience - It has removed a certain amount of the pain from developing the DAO/DB layer boiler plate stuff and let me concentrate on the important stuff about the app. However, one gripe I do have is that it doesn't currently support modelling of many-to-many relationships - the best you can do is create an entity to model what is effectively a join table, then have a one-to-many relationship from that to either side of your "real" many-to-many relationship.

Again, in the above example, we try manage the many-to-many relationship between people and emails (emails can have may people recipients, and people can be recipients to many emails). As mentioned - we have had to model this join explicitly with an EmailPeople entity, and then add the OneToMany relationship between People/Email to our new Join entity.  This is no real headache to do, but is undoubtedly going to be a performance hit - The underlying code will undoubtedly be being cleverly efficient in retrieving all the EmailPeople entities for a given email (assuming attempting to get all recipients to an email) - but from that list we then need an efficient way to load all the People enties linked to that list of EmailPeople entites.
 

Getting Started - A Complete Android App walkthrough for Beginners (Part 2)

This is part 2 of a walkthrough of one of my first ever Android app, that happened to get a little bit of love in the android market, and for which the ENTIRE(!!) source code of the application is available to you good people to do whatever you want with.. see part 1 for more details of the app, what happened and the first steps in getting started!

So far, we have created a simple Activity class, designed a layout with several buttons and then implemented an onClickListener within our Activity class to handle the required actions for all of our buttons. The remaining Activities are all fairly straight forward, and work in a similar fashion: the RulesActivity is just a simple activity what has a block of text with a brief description of the game; the SettingsActivity is a simple activity that has three radio buttons which allows the user to select the difficulty; the QuestionsActivity just displays a question and then a selection of multiple choice answers.

If you are not familiar with Activities then you can open up those other classes and see exactly what is going on, they are all pretty similar but each have slight nuances or differences in their components and actions.

At the moment the application is pretty simple – there is a basic welcome screen with some buttons, and once you start the game you get taken through the QuestionsActivity several times, refreshing with different questions until you have answered all the questions, and then at the end it displays a simple graphic based on your accumulated score. Easy right?!

Really, the only big gap in what is going on is the DB – all the questions need to be stored in the DB on the client device, and then that data needs to be accessed, so we basically need a DAO type class.

In this app, we have a single DBHelper class – you may want to wrap this with a service class, to allow better separation of layers and decreased coupling etc, but for the sake of this app, lets just jump into the DBHelper. To access the DB on Android you need to have your class extend SQLLiteOpenHelper, now lets walk through the code required

private static String DB_PATH = "/data/data/com.tmm.android.chuck/databases/";
private static String DB_NAME = "questionsDb";
private SQLiteDatabase myDataBase; 
private final Context myContext;

First of all we define some simple class member variables. The DB_PATH will be used later to access our file that contains all our questions in it, the DB_NAME is just a name of choice to reference the DB.

public DBHelper(Context context) {
 super(context, DB_NAME, null, 1);
 this.myContext = context;
} 

You have to make sure you override the constructor, as this needs to initialise the context and DB before starting.

Next we have the createDatabase method – this one is quite straight forward:

public void createDataBase() throws IOException{

 boolean dbExist = checkDataBase();
 if(!dbExist)
 {
  this.getReadableDatabase();
  try {
   copyDataBase(); 
  } catch (IOException e) {
   throw new Error("Error copying database");
  }
 }
}


As you can see, it simply checks if the DB already exists, if it doesn’t (in which case its the first time that the app has been launched on this device) then it calls the copyDataBase() method:

private void copyDataBase() throws IOException{

 InputStream myInput = myContext.getAssets().open(DB_NAME);

 String outFileName = DB_PATH + DB_NAME;

 OutputStream myOutput = new FileOutputStream(outFileName);

 byte[] buffer = new byte[1024];
 int length;
 while ((length = myInput.read(buffer))>0){
  myOutput.write(buffer, 0, length);
 }

 //Close the streams
 myOutput.flush();
 myOutput.close();
 myInput.close();
}

Again, this is pretty straight forward – it just opens an Input stream to read the file (our questions file that we have saved in the /assets/ directory) and then writes it to the output stream which has been set up to be our DB Path/Name. Easy right?

So now we pretty much have our DBHelper class that checks for an existing DB, if its found then it opens a connection, if its not then it loads the data (our question set) in for the first time.

Now, the only remaining thing that our helper needs to do is to be able to access the data so we can select questions to show the user. So let’s look at how we add some DAO access methods. What I have done here is create a simple method to return a (random) question set:

public List getQuestionSet(int difficulty, int numQ){
 List questionSet = new ArrayList();

Cursor c = myDataBase.rawQuery("SELECT * FROM QUESTIONS WHERE DIFFICULTY=" + difficulty +
   " ORDER BY RANDOM() LIMIT " + numQ, null);
 
while (c.moveToNext()){
  Question q = new Question();
  q.setQuestion(c.getString(1));
  q.setAnswer(c.getString(2));
  q.setOption1(c.getString(3));
  q.setOption2(c.getString(4));
  q.setOption3(c.getString(5));
  q.setRating(difficulty);
  questionSet.add(q);
 }
 return questionSet;
}

Here, I am modelling the questions in a pre-created “Question” class – this just has the necessary member variables so I can handle the questions more easily than having to try and deal with DB cursors throughout the code. In the above method we just call a pure SQL statement, and then iterate through the cursor to populate my Question ArrayList.

And thats it! There are some other Activity classes and helper classes running doing small things throughout the app, but the core features are covered, so feel free to have a look around the code, edit it, change the design and just generally have fun.