What's in the ORM to you? The near-scientific approach of choosing ORM for Android

    The choice of tools that one way or another will be needed during development is one of the main preparatory stages at the start of a new Android project.
    If you are developing an application that must store a large number of entities in one form or another, you cannot avoid using databases. Unlike colleagues in the workshop developing for iOS, Android programmers do not have convenient tools to facilitate the storage of objects like the Core Data provided by the platform (except for the Content Provider, it will come next about why it doesn’t count). Therefore, many Android developers resort to using third-party ORM solutions in their projects. What you should look at when choosing a library for your project will be discussed in this article.

    To begin with, I would like to make sure that the ORM choice was not made up and we looked at all the available storage tools that are provided by the Android SDK “out of the box”.

    Consider them as the complexity of writing an implementation of such a repository increases. Note that I did not specifically consider ordinary files in this comparison. They, of course, are ideal for lovers of their own bicycles living according to the philosophy of Not Invented Here, but in our case they will be too low-level.

    Shared Preferences
    Key-value storage for primitive data types. Are supported
    Integer, Long, Float, Boolean, String and StringSet. The main purpose is to store a certain state of the application and user settings. At its core, it represents a wrapper over an XML file, which is located in the “private” folder of your application in the shared-prefs subdirectory. It is not suitable for storing a lot of the same structured data.

    SQLite Databases
    SQLite is the standard Android database. The framework provides several helper classes that facilitate working with the database: SQLiteOpenHelper, ContentValues, etc. However, even using these helpers will not relieve you of the obligation to write a huge amount of template code, independently monitor the creation and modification of tables, create methods for operations, methods for searching, etc. Thus, the code of applications that use only standard SQLite tools in Android is becoming increasingly difficult to maintain when adding new and changing old entities.

    Content Provider
    Content Provider is a layer over a real data warehouse. It may seem that the Content Provider is a “boxed” implementation of ORM technology, but this is far from the case. If you use SQLite as a repository for the Content Provider, you will have to independently implement the logic of creating, updating tables, and basic CRUD operations. In most cases, using the Content Provider without special generators will not only not save time on development and support, but, perhaps, will spend it much more than writing your own implementation of SQLiteOpenHelper. However, the Content Provider allows you to use some convenient platform classes - such as AsyncQueryHandler , CursorLoader , SyncAdapter and others.

    We are convinced that we have examined all the data storage tools available in the Android SDK and come to the conclusion: SQLite provides all the necessary conditions for organizing the storage of the same structured data (surprisingly, isn't it?). However, as mentioned above, using SQLite in Android requires a lot of code and constant support, so we’ll try to make our life easier by resorting to a third-party solution.

    This is where the ORM technique, Object Relational Mapping, comes to the rescue. Its implementation, in fact, creates the impression of an object database, having in its basis a regular relational database. ORM, providing a higher level of abstraction, is designed to save programmers from the need to convert data model objects to scalar values ​​supported by the database, allow them to write less boilerplate code and not worry about the structure of tables.

    Having decided on the technology, we will address this question on the Internet and choose 4 libraries:

    How to choose the right library and not regret your decision if it is too late? In similar articles, I came across only high-quality comparisons of libraries. However, in my opinion, the ORM library should be balanced in terms of convenience and performance. Therefore, comparing these solutions only from an API point of view, without performance analysis, would be incomplete. But first, a small digression about why you should pay attention to ORM performance.

    Why all this?

    Why evaluate ORM performance? Obviously, in the end, everything rests on the limitation of SQLite itself, and that, in turn, on the limitation of the file system (we are talking about a single-file database). However, as it turned out, these natural limitations are still very far away.

    Before proceeding to the description of my test and its results, I would like to tell a short story about why we began to pay attention to the performance of ORM, which we use in our projects and attempts to improve it.

    Once to us in Sebbiaa certain application has arrived for development that consumes a unified REST API for all clients. Of all the existing ORMs on the market, it was decided to use ActiveAndroid, which was time-tested and completely satisfying us at that time. The main essence of the application (for simplicity we call it “Essence”) is a certain state of many other system entities, parts of which (“Entity Owners”) were represented only by identifiers of these entities. It was assumed that when requesting “Entities”, the client would download “Entity Owners” automatically if they were not found in the application cache. In mobile devices, I would like to avoid this situation - in view of the energy costs of sending a new request. Any changes to the API would lead to potential compatibility issues with other clients. Therefore, we decided to download and cache the list of “Entity Owners” before it becomes necessary to download the “Entities” themselves. It is most logical to perform such an operation the first time the application is launched. It is worth mentioning that the list of all “Entity Owners” was given in full, and not page by page. What was our surprise when we saw how long this list was stored in the database!

    After checking the code again and making sure that the list is saved once and inside the transaction, ActiveAndroid fell under our suspicion. In general, the reason for the drop in application performance while maintaining a large list was the reflection, namely, getting the values ​​of the object fields and filling them with ContentValues. Replacing the code that uses reflection with the code generated by the self-written plug-in for Eclipse, we got an almost twofold increase in productivity - from ~ 38 seconds to 20. Having made sure once again that it is worth taking a closer look at how open-source libraries are arranged from the inside, let's move on to the content of the article.

    Arms race

    Of all the selected libraries, GreenDao stands apart - after all, it is the only one among the presented solutions who uses code generation. In order not to make quick conclusions - GreenDao is the fastest, and forget about the rest , we decided to arrange the code generation approach (used in the project described above in ActiveAndroid) as a separate fork and pull request to the official repository, supplementing it with other useful functions in parallel: relationship support “One to many”, “many to many” and automatic data migration when changing the structure of entities and, accordingly, their tables. The resulting fork, which I will call “ActiveAndroid.Sebbia” for simplicity, was added to the test.

    It's time to talk about the test. It checks how quickly a particular library can save test entities during an SQLite transaction and performs the reverse operation. 1000 test objects are added to the newly created database, which, after clearing the cache in the ORM memory, are read and checked for data “correctness”. Each subject is checked 10 times, the average time taken for the final result. Test objects consist of two text fields of fixed length, one date field and one byte array obtained from the serializable object. Initially, it was assumed that ORM itself had to convert the Serializable object to an array of bytes, but it turned out that neither GreenDao nor SugarORM had such an opportunity, so I had to abandon this idea.

    In order to understand the maximum possible speed of the “object-row in table-object” operation, which can be achieved using standard Android SDK tools, an example using SQLiteOpenHelper and compiled SQLiteStatement was added to the comparison . The project itself and all version libraries on the basis of which the comparison was made is located on GitHub .

    The results are fairly predictable, No ORM solution is the fastest, although it is not much ahead of GreenDAO. By and large, the code for these solutions is the same, except that GreenDAO provides a more convenient interface.

    In turn, GreenDAO ranks second in the overall standings, but first among ORMs. And this is not surprising. GreenDAO is the only ORM that makes full use of code generation, compiled SQLiteStatements, and other optimizations.
    ActiveAndroid.Sebbia, our fork of ActiveAndroid, using code generation working through Annotation Processor, and SQLiteStatement, takes the second place among ORMs and the third overall. The write operation was almost 4 times faster than the original project, but the reading was not optimized much.

    ActiveAndroid is third in ORM classification, followed by ORMLite-ORM, which came to the Android world from “big” Java, has several plug-ins for working with different data sources and is quite convenient to work with. In last place is SugarORM - the most, in my opinion, unsuccessful of those considered. Firstly, the latest available version from the master branch did not support storing an array of bytes, I had to fix this misunderstanding and rebuild the library, and the pull request was added to the project’s GitHub for a long time , adding this function . Secondly, SugarORM gives the impression of a very heavily functional ActiveAndroid clone (lack of ability to convert objects of other classes and adapters).

    Well, we figured out the performance - code generation is fast, reflection is slow. A call to SQliteDatabase.insert (...) is slower than a call to a pre-created SQLiteStatement. But how convenient is it to use these libraries? Let us dwell on each in more detail.

    Courtyard amenities

    I will begin the review of API of the presented libraries with the champion - GreenDAO .
    As mentioned above, the project is quite unusual compared to other ORMs. Instead of creating the entity classes independently and specifying the fields stored in the table, GreenDAO suggests composing these classes from other code, the generated generation code must be run as a normal Java application. Here's what it looks like:

    public static void main(String[] args) throws Exception {
    	Schema schema = new Schema(1, "com.sebbia.ormbenchmark.greendao");
    	Entity entity = schema.addEntity("GreenDaoEntity");
    	new DaoGenerator().generateAll(schema, "../src-gen/");

    If you need to add your fields in an entity class, you need to put them in blocks marked with special comments:

    // KEEP FIELDS - put your custom fields here
    private Blob blob;

    Similar comment blocks are provided for both methods and imports. If you do not take into account the stunning performance of this approach, then its convenience is extremely doubtful, especially if you use GreenDAO from the first day of development. However, questions arise when using already generated code. For example, why do you need to write so much to get a DAO object:

    DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(context, "greendao", null);
    DaoMaster daoMaster = new DaoMaster(devOpenHelper.getWritableDatabase());
    DaoSession daoSession = daoMaster.newSession();
    GreenDaoEntityDao dao = daoSession.getGreenDaoEntityDao();

    It seems to me too much. It is clear that you will not need to create DaoMaster every time, but still. So, with GreenDAO you will have to perform extra gestures to support the code and use the not the most successful API. However, in return you get speed and nice bonuses like supporting Protobuf objects out of the box.

    Let's move on to ORMLite . ORMLite suggests actively using annotations when declaring their entities:

    @DatabaseTable(tableName = "entity")
    public class OrmLiteEntity implements BenchmarkEntity {
    	@DatabaseField(columnName = "id", generatedId = true)
    	private long id;
    	@DatabaseField(columnName = "field1")
    	private String field1;
    	@DatabaseField(columnName = "field2")
    	private String field2;
    	@DatabaseField(columnName = "blob", dataType = DataType.BYTE_ARRAY)
    	private byte[] blobArray;
    	@DatabaseField(columnName = "date", dataType = DataType.DATE)
    	private Date date;

    Through annotations, you can also set the data type of the field, which is very convenient and does not spread the code associated with the model for the project. The project supports many types of data and their storage options . For example, java.util.Date provides both a numeric and a string version. The disadvantages include the need to implement OrmLiteSqliteOpenHelper, through which you can get a DAO object and interact with ORM. Using separate DAO objects eliminates the need to inherit your entity classes from third-party library objects and allows you to flexibly manage the cache.

    ActiveAndroiduses a similar approach with annotations, but requires model classes to inherit from the Model class they provide. In my opinion, such a solution is optimal in terms of convenience only if your entities are no longer inherited from any class whose parent you cannot change. Such inheritance allows us to have convenient methods like save () and delete () on model objects without creating additional DAO objects. The library also provides date serializers for BigDecimal and other types, and to serialize fields of non-standard types, it is enough to implement your TypeSerializer and specify it during initialization.

    As mentioned above, Sugar ORMgives the impression of a rather weak clone of ActiveAndroid. However, Sugar ORM does not require inheritance from any abstract class and has a fairly concise API:

    public void saveEntitiesInTransaction(final List entities) {
    public List loadEntities() {
    	List entities = SugarRecord.listAll(SugarOrmEntity.class);
    	return entities;

    ActiveAndroid.Sebbiais a fork of ActiveAndroid with support for code generation. In this project, the generation of SQLiteStatement and Cursor binding code to an entity object is generated using the Annotation Processor. Using Annotation Processor instead of the IDE plugin allows you to use it in Eclipse and IntelliJ IDEA, as well as when building a project using Gradle or Ant. However, this imposes a certain restriction on the visibility of the fields of the model classes: the minimum permissible visibility in this case will be without a modifier (default). Code generation made it possible to achieve approximately a 30% increase in productivity; all the rest was due to the precompiled SQLiteStatement. Also, this fork contains OneToManyRelation, ManyToManyRelation and support for automatic migrations, which is used when an SQL migration script for the current version is not found.


    In conclusion, I would like to summarize our small study. ORM is a useful tool that helps you save time while developing your applications. And it is absolutely indispensable in the case of a model with many entities and the relationships between them.
    It is worth remembering that in real life, the end user, most likely, will not see any difference between the fastest and slowest ORM, so is it worth it to think about this - everyone's choice. It remains to add that the solution you choose should be convenient and meet your requirements. In any case, you should follow the general rules when choosing open-source libraries in your projects, namely, evaluate which well-known applications use, what quality of the source code and how it works from the inside.

    Links to repositories:
    Benchmark project

    List of references
    1. Android Storage Options Guide
    2. Object-relational mapping
    3. Java Reflection Performance
    stackoverflow.com/q / 435553/2287859
    4. Is Java Reflection Slow?
    5. Comparing android ORM libraries - GreenDAO vs Ormlite
    6.5 of the Best Android ORMs
    7. Android Orm Benchmark

    Also popular now: