Switching your Android application to Vertabelo Mobile ORM - Tutorial

1. Introduction

Android provides bunch of tools to communicate with database. However, in many cases using them could be very painful. Writing classes and methods to operate on data by hand is very time-consuming and error-prone. With Vertabelo Mobile ORM we can make our lives easier, so let’s take a look how we can switch database-based Android application to use our automatically generated ORM. To simplify, we will use very basic example application called Contact Book. It consists of three activities which enable to view list of all contacts, view details of single contact, add new contacts to the database and remove contacts. Contact Book uses little single-table database but of course using Vertabelo Mobile ORM we can handle much more complicated ones. Here is our our simple schema:

2. What we already have

2.1 POJO object

Contact Book uses several classes to handle database operations. First, we have POJO (Plain Old Java Object) called Contact, which is objective representation of single row in database. Its source code looks as following:

package pl.epoint.contactbook.app.data;

public class Contact {
    private String firstName;
    private String lastName;
    private String number;
    private long id = -1;
    public Contact(String firstName, String lastName, String number) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.number = number;
    }
    public Contact(String firstName, String lastName, String number, long id) {
        this(firstName, lastName, number);
        this.id = id;
    }
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public String getNumber() {
        return number;
    }
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Contact contact = (Contact) o;
        if (!firstName.equals(contact.firstName)) return false;
        return lastName.equals(contact.lastName) && number.equals(contact.number);
    }
    @Override
    public int hashCode() {
        int result = firstName.hashCode();
        result = 31 * result + lastName.hashCode();
        result = 31 * result + number.hashCode();
        return result;
    }
    @Override
    public String toString() {
        return firstName + " " + lastName;
    }
}
                  

2.2 SQLite Open Helper

Android provides abstract class SQLiteOpenHelper which helps creating, upgrading and obtaining access to local SQLite databases. In our example application we have class ContactSQLiteOpenHelper which inherits from SQLiteOpenHelper. We will need it after switching to Vertabelo Mobile ORM as well:

package pl.epoint.contactbook.app.data;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class ContactSQLiteOpenHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "contactBookDatabase";
    private static final int DATABASE_VERSION = 1;
    public static final String TABLE_CONTACTS = "contact";
    public static final String COLUMN_FIRST_NAME = "first_name";
    public static final String COLUMN_LAST_NAME = "last_name";
    public static final String COLUMN_NUMBER = "number";
    private static final String DATABASE_CREATE = "create table "
            + TABLE_CONTACTS + "("
            + "id integer NOT NULL PRIMARY KEY, \n"
            + COLUMN_FIRST_NAME + " varchar(64) NOT NULL, \n"
            + COLUMN_LAST_NAME + " varchar(64) NOT NULL, \n"
            + COLUMN_NUMBER + " varchar(64) NOT NULL \n"
            + ");";
    public ContactSQLiteOpenHelper(Context context)
    {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    @Override
    public void onCreate(SQLiteDatabase database) {
        database.execSQL(DATABASE_CREATE);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
                  

2.3. Contact Access Manager

Then, there is an interface called ContactAccessManager providing operations on contact book such as inserting or looking for contacts. Here is its code:

package pl.epoint.contactbook.app.data;

import java.util.List;

public interface ContactAccessManager {
    long addContact(Contact contact);
    Contact getContact(long id);
    boolean removeContact(long id);
    List<Contact> getContacts();
}
                  

2.4. Contact Access Manager Implementation

There is also an implementation of the interface above called ContactAccessManagerSQLiteImpl. In our example we will modify it’s code using Vertabelo Mobile ORM’s methods.

package pl.epoint.contactbook.app.data;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import java.util.LinkedList;
import java.util.List;

public class ContactAccessManagerSQLiteImpl implements ContactAccessManager {
    private ContactSQLiteHelper helper;
    public ContactAccessManagerSQLiteImpl(Context context) {
        helper = new ContactSQLiteHelper(context);
    }
    @Override
    public long addContact(Contact contact) {
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("first_name", contact.getFirstName());
        values.put("last_name", contact.getLastName());
        values.put("number", contact.getNumber());
        long id = db.insertOrThrow(ContactSQLiteHelper.TABLE_CONTACTS, null, values);
        contact.setId(id);
        return id;
    }
    @Override
    public Contact getContact(long id) {
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.query(ContactSQLiteHelper.TABLE_CONTACTS, null, "id = ?", new String[]{Long.toString(id)}, null, null, null, null);
        if (cursor.getCount() == 0)
            return null;
        cursor.moveToFirst();
        String firstName = cursor.getString(cursor.getColumnIndex("first_name"));
        String lastName = cursor.getString(cursor.getColumnIndex("last_name"));
        String number = cursor.getString(cursor.getColumnIndex("number"));
        cursor.close();
        return new Contact(firstName, lastName, number, id);
    }
    @Override
    public boolean removeContact(long id) {
        SQLiteDatabase db = helper.getWritableDatabase();
        int deleted = db.delete(ContactSQLiteHelper.TABLE_CONTACTS, "id = ?", new String[]{Long.toString(id)});
        return (deleted > 0);
    }
    @Override
    public List<Contact> getContacts() {
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.query(ContactSQLiteHelper.TABLE_CONTACTS, null, null, null, null, null, null, null);
        List<Contact> ret = new LinkedList<Contact>();
        cursor.moveToFirst();
        while (!cursor.isAfterLast())
        {
            String firstName = cursor.getString(cursor.getColumnIndex("first_name"));
            String lastName = cursor.getString(cursor.getColumnIndex("last_name"));
            String number = cursor.getString(cursor.getColumnIndex("number"));
            long id = cursor.getLong(cursor.getColumnIndex("id"));
            ret.add(new Contact(firstName, lastName, number, id));
            cursor.moveToNext();
        }
        cursor.close();
        return ret;
    }
}
                  

2.5. Summary

For our simple database this implementation works nice, but in real life this approach can create big problems. For every database operation we want to use in application we need to write a method by hand, using low-level operations. This is not only time-consuming and error-prone but also difficult to maintain. In case of database schema's change, we have to modify complicated data access methods. This can be real hell.

3. Switching to Vertabelo Mobile ORM

3.1. Introduction

Let’s modify our application so that it will use Vertabelo Mobile ORM to perform database operations. First, we have to copy all the classes provided by Vertabelo Mobile ORM to our project. We can remove all the POJOs (in our case the only one) and supersede them with newly generated ones.

3.2. Contact Access Manager SQLite Implementation

Now we should make ContactAccessManagerSQLiteImpl use methods provided by ORM. Now we don’t have to write implementations by hand because they’re already automatically generated. New version of the class is much shorter and looks clearer:

package pl.epoint.contactbook.data;
import android.content.Context;
import pl.epoint.mobiorm.android.runtime.util.SimpleSQLiteDataSource;
import java.util.List;
public class ContactAccessManagerSQLiteImpl implements ContactAccessManager {
    private ContactDAO contactDAO;
    public ContactAccessManagerSQLiteImpl(Context context) {
        ContactSQLiteOpenHelper helper = new ContactSQLiteOpenHelper(context);
        contactDAO = new ContactDAOImpl(new SimpleSQLiteDataSource(helper.getWritableDatabase())); 
    }
    @Override
    public long addContact(Contact contact) {
        return contactDAO.insert(contact);
    }
    @Override
    public Contact getContact(long id) {
        return contactDAO.getContact(Long.valueOf(id).intValue());
    }
    @Override
    public void removeContact(long id) {
        contactDAO.delete(contactDAO.getContact(Long.valueOf(id).intValue()));
    }
    @Override
    public List<Contact> getContacts() {
        return contactDAO.getContactList();
    }
}
                  

3.3. Final advices

Note that in order to create DAO instance we're still using the same implementation of android.database.sqlite.SQLiteOpenHelper as in previous version of our app. If you're writing your application from scratch we recommend you to use pl.epoint.mobiorm.tutorial.orm.runtime.util.SimpleSQLiteOpenHelper which is provided by Vertabelo Mobile ORM. Also, in real situation, when we have multiple tables in database schema, it's inconvenient to create new instance of DAO object each time we use it. Vertabelo Mobile ORM comes with class called pl.epoint.mobiorm.tutorial.orm.gen.DAOProvider which helps obtaining access to DAO objects. You can learn more about these features in Android tutorial and DAO Guide.

4. Summary

In fact, that’s everything we should do to make our example application use Vertabelo Mobile ORM. Of course, it uses only little sample of features provided by our library. With Vertabelo Mobile ORM you can handle much more complicated cases as it provides all you need to problem-freely communicate with your database.