There are several popular ORM database libraries out there (Active Android, Sprinkles, GreenDAO, SugarORM) and nearly every one uses reflection for critical database interactions. We built DBFlow as a more efficient and powerful alternative, and the results have been outstanding – it’s the fastest Android ORM database library.

Reflection And Its Risks

Reflection is “commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine”1. In short, it allows us to retrieve properties and data from objects at runtime by inspecting the contents. With this information, for example, we can match a field from a Model object with a column in the database from a specific table.

Performance is a major area of concern with utilizing a data-heavy backend in an application. According to the Java documentation, reflection can’t be optimized by the compiler, and thus takes significant performance hit when choosing it over native execution1.

Annotation Processing: A More Efficient Alternative

In Java, annotation processing is a step run before compile time that gathers all annotations defined for a project and enables processors to perform some action. Writing an annotation processor is no easy task, and there are two significant roadblocks: the learning curve and writing the necessary code. The learning curve is daunting because the Annotation Processing API contains a set of unfamiliar and different name classes than its counterpart, the Reflection API. Second, we have write code that writes code that compiles correctly. From this, writing the processor becomes trial and error until the code compiles correctly and runs as expected.

DBFlow uses this functionality to generate all sorts of classes and interactions with the database at compile time. This enables the library to run at native speed and becomes as fast as writing the code yourself. Also, generating code is transparent–we can see the code that the app executes and catch errors at compile time. Reflection is difficult to debug, since we will only catch errors at runtime.

DBFLOW: An Overview

A wrapper API for SQLite statements, so writing queries with DBFlow feels natural



List devices = new Select().from(DeviceObject.class) .where( Condition.column(DeviceObject$Table.NAME).is("Samsung-Galaxy-S5"), Condition.column(DeviceObject$Table.CARRIER).is("T-Mobile")).queryList(); Delete.table(DeviceObject.class); 1 2 3 4 5 6 List devices = new Select ( ) . from ( DeviceObject . class ) . where ( Condition . column ( DeviceObject $ Table . NAME ) . is ( "Samsung-Galaxy-S5" ) , Condition . column ( DeviceObject $ Table . CARRIER ) . is ( "T-Mobile" ) ) . queryList ( ) ; Delete . table ( DeviceObject . class ) ;

Seamless and simple multi-database support by associating Model classes with a table that exists in that database.



To define a database:

@Database(name = AppDatabase.NAME, version = AppDatabase.VERSION) public class AppDatabase { public static final String NAME = "App"; public static final int VERSION = 1; } 1 2 3 4 5 @Database ( name = AppDatabase . NAME , version = AppDatabase . VERSION ) public class AppDatabase { public static final String NAME = "App" ; public static final int VERSION = 1 ; }

To define a table that corresponds with that database we define:

@Table(databaseName = AppDatabase.NAME) public class TestModel1 extends BaseModel { @Column(columnType = Column.PRIMARY_KEY) public String name; } 1 2 3 4 5 6 @Table ( databaseName = AppDatabase . NAME ) public class TestModel1 extends BaseModel { @Column ( columnType = Column . PRIMARY_KEY ) public String name ; }

A transaction queue, which will run and queue up database requests in the same thread using the TransactionManager. The transaction queue is ordered by priority so that the most important database interactions can be prioritized.

Full SQLite VIEW support

@ModelView(query = "SELECT * FROM TestModel2 WHERE model_order > 5", databaseName = TestDatabase.NAME) public class TestModelView extends BaseModelView { @Column long model_order; } 1 2 3 4 5 @ModelView ( query = "SELECT * FROM TestModel2 WHERE model_order > 5" , databaseName = TestDatabase . NAME ) public class TestModelView extends BaseModelView { @Column long model_order ; }

First-class Migration support



Migrations are defined by creating a Migration class or by placing a valid SQL statement file in your project’s “assets/migrations/{DatabaseName}/{versionName.sql}” directory. Here is an example of a migration class:

@Migration(version = {versionOfMigration}, databaseName = {DatabaseName}) public class Migration1 extends BaseMigration { @Override public void migrate(SQLiteDatabase database) { } } 1 2 3 4 5 6 7 8 @Migration ( version = { versionOfMigration } , databaseName = { DatabaseName } ) public class Migration1 extends BaseMigration { @Override public void migrate ( SQLiteDatabase database ) { } }

Support for type conversion, which allows non-standard SQLite fields to save to the database.

Model Containers: classes that imitate and use the blueprint of “Model” classes in order to save data such as JSON. They interact with the database directly without having to convert the data out of its native format before saving.



JSONModel jsonModel = new JSONModel(json, TestObject.class); // constructs an insert or update query based on the JSON contents jsonModel.save(false); // deletes a model based on the JSON contents jsonModel.delete(false); 1 2 3 4 5 6 7 JSONModel jsonModel = new JSONModel ( json , TestObject . class ) ; // constructs an insert or update query based on the JSON contents jsonModel . save ( false ) ; // deletes a model based on the JSON contents jsonModel . delete ( false ) ;

Ultra-efficient relationships: Lazy-loading for maximum performance using ForeignKeyContainers, powerful model caching support, and much more.

Results: The fastest android orm database library.

To show DBFlow’s performance optimization, I performed a series of speed tests in an Android emulator using a few of the most popular Android database libraries2. The test consisted of creating and saving 25,000 records to the database in one large transaction3. I specifically recorded the time it took to save all of the models, and not the time it took to create all 25,000 of them. Afterwards, I record the time it took to load all of them out of the database. The model, AddressItem, had a simple schema:

id INTEGER PRIMARY KEY AUTOINCREMENT name TEXT address TEXT city TEXT state TEXT phone INTEGER 1 2 3 4 5 6 id INTEGER PRIMARY KEY AUTOINCREMENT name TEXT address TEXT city TEXT state TEXT phone INTEGER

In our tests DBFlow was the fastest Android ORM database library, consistently performing more than twice as fast as the closest competitor. By using DBFlow, your application will feel snappier, you will decrease your application’s power footprint, and the code you write will feel natural and simple. DBFlow is open source and ready for you to use. Give it a spin and let us know what you think!

Download

Check DBFlow out on Github, or by using gradle, add the Griddle plugin and our maven repo to your root build.gradle:

buildscript { repositories { maven { url "https://raw.github.com/Raizlabs/maven-releases/master/releases" } } classpath 'com.raizlabs:Griddle:1.0.2' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } allprojects { repositories { maven { url "https://raw.github.com/Raizlabs/maven-releases/master/releases" } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 buildscript { repositories { maven { url "https://raw.github.com/Raizlabs/maven-releases/master/releases" } } classpath 'com.raizlabs:Griddle:1.0.2' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } allprojects { repositories { maven { url "https://raw.github.com/Raizlabs/maven-releases/master/releases" } } }

Add the library to the project-level build.gradle, using the APT plugin and the Griddle plugin:

apply plugin: 'com.neenbedankt.android-apt' apply plugin: 'com.raizlabs.griddle' dependencies { apt 'com.raizlabs.android:DBFlow-Compiler:1.4.5' mod "com.raizlabs.android:{DBFlow-Core, DBFlow}:1.4.5" } 1 2 3 4 5 6 7 apply plugin : 'com.neenbedankt.android-apt' apply plugin : 'com.raizlabs.griddle' dependencies { apt 'com.raizlabs.android:DBFlow-Compiler:1.4.5' mod "com.raizlabs.android:{DBFlow-Core, DBFlow}:1.4.5" }

1. http://docs.oracle.com/javase/tutorial/reflect/

2. I used ones that I was most familiar with and those that were more than 500 stars on GitHub.

3. This number seems unusually high. However, when we deal with more complicated models and add relationships between tables, the complexity approaches the test that we do here.

Interested in joining the Raizlabs team making great software? We’re hiring Android developers in Boston and SF.