Spiria logo.

Data persistence under Android

October 9, 2015.
public class UserDAO {
    private Dao userDao = DatabaseHelper.getDao(User.class);

    public static boolean saveUser(User user) {
        try {
            userDao.saveOrUpdate(user);

            return true;
        } catch (SQLException e) {
            // Log exception here.
            return false;
        }
    }
}

What is an MRO? An ORM, for Object Relational Mapping, is a tool for object-oriented programming to create a database representation of an object. Let’s take the example of the following object:

What is an MRO?

An ORM, for Object Relational Mapping, is a tool for object-oriented programming to create a database representation of an object. Let's take the example of the following object:

public class User {
    private String name = null;
    private int age = -1;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

With an ORM this object will become a "User" table containing the fields "name" and "age". These fields will be typed according to the database engine used (for example, the "name" field will certainly be a "varchar" if the project is "mapped" on an Oracle database). The interest is thus to have a "object" approach of its database. The persistence of the instances of these objects is done using a dedicated component of the ORM called a DAO for Data Access Object. The role of this DAO will be to save (persist) the instances of the "mapped" objects. Taking the example from before, if I use my object as such...

User johnDoe = new User("John", 20);

... I could then persist by calling his DAO:

UserDAO.saveOrUpdate(johnDoe);

When the code is executed, a new entry will be added to the "User" table of the database and will contain the information of the instance. Its "name" field will then be worth "John" and its "age" field "20".

How to use an ORM with my Android project?

For Android, there is a particularly easy to use ORM called ORM Lite available at the following address: http://ormlite.com/.
You will need to download the latest versions of the following two files:

  • ormlite-android-x.xx.jar
  • ormlite-core-x.xx.jar


Then you just have to create a "libs" folder at the root of your Android project and put both archives there. Depending on your IDE and the version of Android you are using, you may have to reference them manually.
For example, under Eclipse, by right-clicking on your project in:
-Properties
--Java Build Path
---Libraries
----Add Jar
-----Allez dans le dossier ou vous avez déposé vos Jars puis ajoutez les.

What do I need in my code?

The architecture most frequently encountered with ORM Lite is the following:

  • In a "Data" package, add a "Entities" sub-package to get the structure " com.myORMLiteProject.Data.Entities ".
  • In the package "com.myORMLiteProject.Data" create two new empty classes with the names "DatabaseHelper.java" and "DatabaseManager.java".
  • The "DatabaseHelper.java" file will be used to update the database, ensure its creation and provide CADs for the objects and anything else related to the database itself.
  • As for the "DatabaseManager.java" file, it will be used to encapsulate the CADs in order to facilitate their use. It will contain, for example, a static method "saveUser(User)" which, in its internal mechanics, will use the CAD of the "User" entity to carry out its persistence. This file is of interest only for small projects with no more than a few CADs, which should be enough for mobile projects. For larger projects, it will be strongly advised to create a "DaoFoo" object for each object that will be instantiated and called separately if needed...

For example :

public class UserDAO {
    private Dao userDao = DatabaseHelper.getDao(User.class);

    public static boolean saveUser(User user) {
        try {
            userDao.saveOrUpdate(user);

            return true;
        } catch (SQLException e) {
            // Log exception here.
            return false;
        }
    }
}

Entities, on the other hand, are simple "PoJo" using annotations to define the fact that they are entities and what their fields are. They must have an "id" field that will always be declared in the same way, as well as an empty public constructor. In our case, the "User.java" entity could look like this:

@DatabaseTable
public class User {
    // ******************************
    // Fields
    // ******************************
    @DatabaseField(generatedId = true)
    private int id;
    @DatabaseField
    private String name;
    @DatabaseField
    private int age;
    
    // ******************************
    // Constructor(s)
    // ******************************
    /**
     * Default constructor.
     * 
     * @param name The {@link #User}'s name.
     * @param age The {@link #User}'s age.
     */
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    /**
     * Empty constructor.

     * Used by ORMLite for mapping purpose.
     */
    public User() {
    
    }
    
    // ******************************
    // Getters & setters
    // ******************************
    /**
     * Returns the {@link #User}'s identifier.
     * 
     * @return The {@link #User}'s identifier.
     */
    public int getId() {
        return id;
    }
    
    /**
     * Returns the {@link #User}'s name.
     * 
     * @return The {@link #User}'s name.
     */
    public String getName() {
        return name;
    }
    
    /**
     * Sets the {@link #User}'s name.
     * 
     * @param name The {@link #User}'s name.
     */
    public void setName(String name) {
        this.name = name;
    }
    
    /**
     * Returns {@link #User}'s age.
     * 
     * @return {@link #User}'s age.
     */
    public int getAge() {
        return age;
    }
    
    /** 
     * Sets {@link #User}'s age.
     * 
     * @param age {@link #User}'s age.
     */
    public void setAge(int age) {
        this.age = age;
    }
}

As seen previously, here is an example of what the "DatabaseHelper.java" file might look like:

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
    // ******************************
    // Constants
    // ******************************
    private static final String DATABASE_NAME = "MyOrmliteProject.sqlite";
    private static final int DATABASE_VERSION = 1;
    
    // ******************************
    // Fields
    // ******************************
    private Dao userDao = null;
    
    // ******************************
    // Constructor(s)
    // ******************************
    /**
     * Default constructor.
     * 
     * @param context The application's {@link #Context}.
     */
    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    
    // ******************************
    // Methods
    // ******************************
    @Override
    public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
        try {
            TableUtils.createTable(connectionSource, User.class);
        } catch (java.sql.SQLException e) {
            Log.e("Can't create database!", e);
        }
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
        try {
            List allSql = new ArrayList();
            
            switch (oldVersion) {
                case 1:
                    // allSql.add("alter table AdData add column `new_col` VARCHAR");
                    // allSql.add("alter table AdData add column `new_col2` VARCHAR");
                    break;
                case 2:
                    break;
                case 3:
                    break;
            }
            
            for (String sql : allSql) {
                db.execSQL(sql);
            }
        } catch (SQLException e) {
            Log.e("Can't update database!", e);

            throw new RuntimeException(e);
        }
    }
    
    /**
     * Returns the {@link #User}'s data access object.
     * 
     * @return The {@link #User}'s data access object.
     */
    public Dao getUserDao() {
        if (null == userDao) {
            try {
                userDao = getDao(User.class);
            } catch (java.sql.SQLException e) {
                Log.e("Can't retrieve DAO!", e);
            }
        }
        
        return userDao;
    }
}

And an example of the utility class "DatabaseManager.java" which encapsulates the use of CADs. It should be emphasized again that in case the project contains more than 2 or 3 entities, it will be much better to use a CAD object dedicated to each of them. For example, here we have a "UserDAO.java" (which in this example would contain the same thing as "DatabaseManager.java"), and if we had another entity managing blocks, we would have a "ParpaingDAO" which would only contain accesses relative to its DAO and none relative to the "User" object:

public class DatabaseManager {    
    // ******************************
    // Fields
    // ******************************
    private static DatabaseHelper databaseHelper = null;
    
    // ******************************
    // Private methods
    // ******************************
    private static DatabaseHelper getDatabaseHelper(Context context) {
        if(databaseHelper == null) {
            databaseHelper = new DatabaseHelper(context);
        }
        
        return databaseHelper;
    }
    
    // ******************************
    // Public methods
    // ******************************
    /**
     * Saves or updates the given {@link #User} in the database.
     * 
     * @param context The application's {@link #Context}.
     * @param user The {@link #User} to save or update.
     * @return true if the given {@link #User} was correctly saved or updated, false otherwise.
     */
    public static boolean saveOrUpdateUser(Context context, User user) {
        try {
            getDatabaseHelper(context).getUserDao().createOrUpdate(user);

            return true;
        } catch (SQLException e) {
            Log.e("Can't save or update the given User!", e);

            return false;
        }
    }
    
    /**
     * Deletes the given {@link #User} from the database.
     * 
     * @param context The application's {@link #Context}.
     * @param user The {@link #User} to delete.
     * @return true if the given {@link #User} was correctly removed from the database.
     */
    public static boolean deleteUser(Context context, User user) {
        Dao userDao = getDatabaseHelper(context).getUserDao();
        
        try {
            userDao.delete(user);

            return true;
        } catch (SQLException e) {
            Log.e("Can't delete the given User!", e);

            return false;
        }
    }
    
    /**
     * Returns the first {@link #User} from the database, null if not found.
     * 
     * @param context The application's {@link #Context}.
     * @return The first {@link #User} present in database, null if not found.
     */
    public static User getUser(Context context) {
        Dao userDao = getDatabaseHelper(context).getUserDao();
        List result = null;
        
        try {
            result = userDao.queryForAll();
        } catch (SQLException e) {
            Log.e("Can't retrieve the first User!", e);
        }

        if(result != null && result.size() > 0) {
            user = result.get(0);

            return user;
        }
        
        return null;
    }
}

Example of an Android application.

You will find here https://github.com/Romain41/RunRun an implementation of what has been seen. This sample application will record running races made by a single user and output them as a list. It is an Eclipse project that you will just have to import into your IDE as an existing Android project.