Using db4o in an Android application
db4o is an object database, ie. forget about mapping of tables in a relational model. If you're a developer that translates into savings in time invested in your application and volume of code. db4o's great potential is that you can reuse your (plain, non-mapped) objects by saving and retrieving them as many times as you want. You can persist complex objects with nested collections or other complex objects, any level of complexity in your hierarchy. You're not limited to flat objects with primitive types to get a reasonable performance.
With db4o you can also benefit from Native Queries which brings you closer to the language and combine optimized execution with simplicity even for complex queries. db4o is open source and can be downloaded here.
In order to use db4o in your application you can simply reference a single jar file in your project and that's it (no setup, no server, no complex installation). For a java project using Eclipse you just drop the jar file in the "lib" folder and then add the library to the build path. According to your needs you must choose the db4o-xxx-yyy.jar file to include. If "xxx" is "all" you get all the functionality of db4o. If it's "core" you get the very minimum. With regards to "yyy" depends on what version of Java you need to support (eg. db4o-xx-core-java5.jar targets Java JDK 5 and JDK 6)
How to use db4o in Android
I really enjoy working with db4o on Android. I usually start by creating a central class (eg. Db4oHelper) that handles the db4o API and controls when to create, open and close the database.
public class Db4oHelper {
private static ObjectContainer oc = null;
private Context context;
/**
* @param ctx
*/
public Db4oHelper(Context ctx) {
context = ctx;
}
/**
* Create, open and close the database
*/
public ObjectContainer db() {
try {
if (oc == null || oc.ext().isClosed()) {
oc = Db4oEmbedded.openFile(dbConfig(), db4oDBFullPath(context));
//We first load the initial data from the database
ExercisesLoader.load(context, oc);
}
return oc;
} catch (Exception ie) {
Log.e(Db4oHelper.class.getName(), ie.toString());
return null;
}
}
/**
* Configure the behavior of the database
*/
private EmbeddedConfiguration dbConfig() throws IOException {
EmbeddedConfiguration configuration = Db4oEmbedded.newConfiguration();
configuration.common().objectClass(Exercise.class).objectField("name").indexed(true);
configuration.common().objectClass(Exercise.class).cascadeOnUpdate(true);
configuration.common().objectClass(Exercise.class).cascadeOnActivate(true);
return configuration;
}
/**
* Returns the path for the database location
*/
private String db4oDBFullPath(Context ctx) {
return ctx.getDir("data", 0) + "/" + "pumpup.db4o";
}
/**
* Closes the database
*/
public void close() {
if (oc != null)
oc.close();
}
}
}
Then we can create a Provider for each of our classes where we can implement the associated queries (eg. ExerciseProvider.java to handle persistence of Exercises). We can make it extend Db4oHelper to get all the maintenance methods (open, close, etc).
public class ExerciseProvider extends Db4oHelper {
public final static String TAG = "ExerciseProvider";
private static ExerciseProvider provider = null;
public ExerciseProvider(Context ctx) {
super(ctx);
}
public static ExerciseProvider getInstance(Context ctx) {
if (provider == null)
provider = new ExerciseProvider(ctx);
return provider;
}
public void store(Exercise exercise) {
db().store(exercise);
}
public void delete(Exercise exercise) {
db().delete(exercise);
}
public List findAll() {
return db().query(Exercise.class);
}
}
It's really important not to forget to close the database before exiting the application, otherwise your changes won't be committed. A nice way to implement this in Android is in the Launcher activity (main activity for the application) where we'll create an instance of Db4oHelper and invoke close() upon exit.
public class main extends Activity {
private Db4oHelper db4oHelper = null;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
dbHelper();
}
/**
* Create Db4oHelper instance
*/
private Db4oHelper dbHelper() {
if (db4oHelper == null) {
db4oHelper = new Db4oHelper(this);
db4oHelper.db();
}
return db4oHelper;
}
/**
* Close database before exiting the application
*/
@Override
protected void onPause() {
super.onDestroy();
dbHelper().close();
db4oHelper = null;
}
}
Bottom line
Using db4o on Android is a really nice option if you're not forced to work with ContentProviders (which are only really necessary if you must share data with other external applications and are too coupled to Android's SQLite). For handling all persistence aspects of a single application db4o is a pleasure to work with (no headaches, saved a lot of time, simple and straight-forward!)
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Lucas Godoy replied on Wed, 2010/06/23 - 9:37am
I have the idea that this kind of approach is slower than SQLite. Is it true?
Good article.
Eric Galluzzo replied on Wed, 2010/06/23 - 10:01am
Your Provider class seems like a DAO. You might consider using a generified DAO to save yourself some boilerplate code, along the lines of the following. We did this recently on a JPA project and it helped greatly.
public class DbProvider<T extends Serializable> extends Db4oHelper {
public Class<T> persistentClass;
public DbProvider( Class<T> persistentClass, Context ctx ) {
super( ctx );
this.persistentClass = persistentClass;
}
public void store( T obj ) {
db().store( obj );
}
public void delete( T obj ) {
db().delete( obj );
}
public List<T> findAll() {
return db().query( persistentClass );
}
}
public class ExerciseProvider extends DbProvider<Exercise> {
public ExerciseProvider( Context ctx ) {
super( Exercise.class, ctx );
}
// Exercise-specific methods here
}
Even better, you might consider using interfaces so that you can plug in whatever persistence provider (Db4o, SQLite, ...) you want. This might be overkill for an Android app, though.
German Viscuso replied on Wed, 2010/06/23 - 10:26am
in response to:
Lucas Godoy
Hi Lucas. I believe it's the other way round, first the mapping (table <-> object) process takes time and second db4o uses b-tree indexes for fast access. I can verify this on a simple benchmark that compares db4o vs SQLite where db4o is faster (taken from a benchmark published by the guys that provide Perst). If you want to actually try the benchmark please contact me privately and I'll send it to you.
Moreover the simplicity and time saved are pretty obvious here: http://developer.db4o.com/Projects/html/projectspaces/android_password_manager.html (see the code comparisons, the video sucks)
Best!
German Viscuso replied on Wed, 2010/06/23 - 10:23am
in response to:
Eric Galluzzo
Hi Eric. Nice use of generics, I believe it quite works to simplify the example. But I agree that using interfaces so that you can plug in whatever persistence provider you want might be overkill for Android (can't really say until we see code).
Best.
Jan Kotek replied on Thu, 2010/06/24 - 6:03am
Checkout JDBM2 http://jdbm2.googlecode.com. It is under Apache2 license and JAR file have only 130KB
German Viscuso replied on Fri, 2010/06/25 - 10:41am
in response to:
Jan Kotek
Damasia Maneiro replied on Fri, 2010/06/25 - 1:25pm
in response to:
Eric Galluzzo
Stuart Merrell replied on Fri, 2010/07/02 - 3:30pm