📜 ⬆️ ⬇️

Comparative testing of nine ORM for Android

On the Internet, a lot of fragmentary information about this or that ORM for Android. So far, I have not come across a quality comparison of leading ORMs. Existing articles smack of PR of one system or another and put their competitors at a disadvantage either by incorrectly testing, or by using obviously wrong settings, or by not including strong opponents in testing.

This testing was carried out more for self-interest. Since There is a lot of ORM, they are all different, and I would like to have an objective view of the existing systems.

The basis was taken repository AndroidDatabaseLibraryComparison . Here is their original article . Apparently, the author first tried not to include GreenDao in the review, and when he was asked to turn it on several times with absolutely wrong settings, as a result, the “superfast Android ORM” turned out to be almost the slowest.

Separately, I want to note that most ORMs are still not complete, and very rarely does a library support writing and reading an object with nested collections without dances with annotations and separate subqueries.
')

Testing method


2 models were tested. Simple, so-called. POJO :

Simple
public class SimpleAddressItem{ String name; String address; String city; String state; long phone; } 


Simple types, linear structure. This object was created in 10,000 copies, the copies were placed in the collection and the collection was saved using ORM tools in the database. Repeated 6 times.

At the reading stage, the collection was read, the read fields were reconciled firstly to control that the data was written and read, secondly to avoid cunning Lazy readings. After each readout, the application was closed through the task manager and restarted. The fact is that some ORMs such as Realm, DBFlow and GreenDao have their caches. And if you read immediately after the recording, or 2 times in a row, the reading of their cache occurs almost instantly, which distorts the overall result. Yes, this can be considered as an advantage and should be considered when choosing ORM.

Complicated model. Object with nested lists:

Complex
 public class AddressBook{ Long id; String name; String author; Collection<AddressItem> addresses; Collection<Contact> contacts; } public class AddressItem extends SimpleAddressItem { AddressBook addressBook; } public class Contact{ String name; String email; AddressBook addressBook; } 


The object was created in 50 instances. Each nested collection was also filled with 50 copies. Recording was made. When reading the data was checked for correctness.

Each test, again, was performed 6 times. In the resulting graphs, the numbers for the columns are the averaged values ​​of the execution time.

results


There are a lot of pictures, so under the spoiler:

results
Simple model. Record:



Simple model. Reading:



Simple model. Reading without Sugar ORM:



Complicated model. Record:



Complicated model. Reading:



Complicated model. Reading without Sugar ORM:



Test participants


1. Realm


Really very fast. Indeed, in some cases, faster than pure SQLite (it should be noted here that SQL used a simple flat algorithm. If you think about it, you can reduce the number of calls to the database by using LEFT JOIN, but you had to think, then debug, and I wasn’t hunting, so if there is a desire - welcome). What should be especially emphasized is a very simple implementation. Intuitive. A huge plus is that Realm is a full ORM. Those. save an object / read an object: no need to think about internal collections, their ratios, annotate with insane "Foreign ...", "One-to-many ..." keys and other bells and whistles. Everything works their boxes.

But there are also disadvantages. It all the same there are strange falling with an assembly stack in the console. Yes, they are very rare, but there is. All fields must be private, there must be an empty constructor and getter + setter to each field. Of course, Lombok support appeared in recent versions, but this is another +1 library and +1 plug-in to the project. Of course, and without Lombok, this is done in 3 shortcuts in Android Studio, but this is still a bundle of boilerplate code to you in the project. Speaking of size: +5 megabytes to apk just for connecting Realm.

You should also consider the compile-time classes that do not exist before you build the project for the first time. Difficulties with Parceler, RealmResult and RealmObject differences, inability to read an object after realm.close (). The RealmList / RealmResult collections, though inherited from List, do not support many basic methods, such as addAll, indexOf, toArray. All this adds additional problems. Of course, they can be solved, but the complexity of the project increases and this should also be taken into account.

In general, working with Realm is nice. Plus: good documentation, almost all issues can be solved by reading the official site.

2. OrmLite


Very old and well-proven ORM. One problem: the last release was 3 years ago. Much obsolete. The approach itself is outdated. Code generation with compile-time annotations in 2016? Do you seriously want to get involved in this? In general, it shows itself as an ordinary middling. Somewhere not 5-6th place. The only nice feature: the reading of nested collections automatically when reading the parent. Just like Realm. Only slower. But other ORMs do not have this.

3. GreenDao


The superfast Android ORM for SQLite. So stated on the official website. And he is really superfast. Sometimes even faster Realm. But again, code generation. You will have to create a database in a separate project, with the help of additional commands, then compile it and only then you will have classes with which you can work. Generated classes can be modified, but it should be remembered that if you do not include special comments, they will all be created in a new way when they are regenerated. The library itself is fast and small. So there are pluses and minuses. To use or not - you decide.

4. DBFlow


Another favorite for speed (but not for convenience). Very fast, as the authors stated - FASTEST ANDROID ORM DATABASE LIBRARY, but, we see, it is not. Annotation processing, i.e. code generation during compilation. As a result, for example, save () turns into sql compileStatement commands, which provides the greatest performance. The loss of speed of course in factories and reflection, where do without it. Supports results caching, which allows you to retrieve data from the cache almost instantly.

But there are also disadvantages: the library is developing, now in the release the 3rd beta version is not compatible with the 2nd. Half of the official documentation for the 2nd, there are few examples, the official documentation is incomplete and sometimes even with errors. Glitches climbs in the most unexpected places. For example, if you call the name pakage, which contains your class with a capital letter, your project will not compile. And you won't even know why. There are no normal text errors. Migration problems. Maybe if you sit a little longer, something will turn out, but my migration did not take off, although I did everything by examples. In general, you need to invest a lot of man-hours to start using this library normally. What kind of example to go far, the authors themselves did not have a working example in this test, I had to finish it.

5. Cupboard


Simple, not very slow, sometimes even fast. It works even without annotations! But for those who want, of course, there are all sorts of @Ignore and even Index . The only problem is working not from the object, but from the database. Those. To write an object, you need to do something like the following manipulations:

 CupboardDatabase database = new CupboardDatabase(context); SQLiteDatabase db = database.getWritableDatabase(); DatabaseCompartment dbc = cupboard().withDatabase(db); dbc.put(addresses); 

What is not always convenient. Plus you should definitely get a Long _id field for each object.

6. Sprinkles


The library was not added to the review by me, but I completed the missing tests. The library is terrible. Just awful. Just look at the graphics. Do you know why? Because the authors make "SELECT * FROM% s WHERE% s LIMIT 1" for EVERY record in the database! Yes Yes. This authors emulate "INSERT OR REPLACE". And you have no choice. You have only Save () and all. Reading is a simple wrapper over sql: Query.many (AddressItem.class, "select * from AddressItem where addressBook =?", String.valueOf (id)). Get (). AsList (). Is it an ORM? True? The mechanism for creating a base is not at all. Write "CREATE TABLE" manually.

7. ActiveAndroid


Well, what can I say. Just ORM without benefits. The speed is no different.

8. Ollie


Another compile-time ORM. In general, quite smart. I cannot say anything about the advantages / disadvantages, because did not work.

9. Sugar ORM


Simple, affordable, but terribly slow. Reflection on reflection with runtime annotation readings translates into very slow work. Until recently, I wrote Log.i (SUGAR, object.getClass (). GetSimpleName () + "saved:" + id); "for each entry in the database. If you remove this line - sometimes write speed increases by 2 times! It seems the authors are not concerned with performance for the sake of simplicity. But with simplicity, everything is in order: a minimum of annotations, simple save (), find (), though, who will surprise with this?

10. SQLlite.


Here we will talk about pure SQL, aimed at maximum speed. These are “compileStatement” and bindString (i, value), yes, yes, by index, not by name. And no ContentValues. Just not on my shift!

SQLlist, of course, is almost always the leader in its pure form. The only exception is Realm on the operations of complex reading, which probably gives the nosql structure of the base to know. But, as I mentioned above, sure it can be optimized.

Someone will ask what the column “SQLite 50x” means on the first chart? This is a multi-row entry, when each insert into the database is made of 50 objects:

INSERT INTO table VALUES (? 1,? 2,? 3), VALUES (? 4,? 5,? 6), VALUES (? 7,? 8,? 9) ...

This greatly complicates the code, so I made it just for a simple test. In the github project, this code is commented out.
Also on pure SQL, the influence of indexes is particularly noticeable, and how writing to an indexed table slows down, and how reading is accelerated. Well, this is truisms and the use of indices everyone must choose a task.

Conclusion


Certainly testing is not complete. Certainly not taken into account the speed of random reading. There is also no testing for ORM initialization speed, i.e. the time from the creation of the ORM wrapper itself to the readiness to work with the database. There is a NoSQL like Snappy-DB that would be interesting to see in action. There are new ORMs like requery , about which too little is known, but their statements are as always loud (support for RxJava, reflection-free, etc.).

All this takes time and, if there is a wish, the project code is open: github.com/Rexee/AndroidDatabaseLibraryComparison

Source: https://habr.com/ru/post/281226/


All Articles