5 лучших ORM для Android

Original author: Aldo Ziflaj
  • Translation


Как вы понимаете, мы имеем самое непосредственное отношение к разработке приложений для Android, ведь именно эта ОС установлена на YotaPhone. И этим постом мы открываем серию публикаций, в которых будем освещать различные аспекты разработки приложений под Android, делиться опытом, как своим, так и других специалистов. Кстати, некоторое время назад мы уже писали об SDK и особенностях архитектуры YotaPhone, что тоже отчасти перекликается с темой данной публикации: полностью разработанная нами SDK построена по принципам, аналогичным стандартной SDK для Android, чтобы дать разработчикам интуитивно понятные инструменты.

Если вы разрабатываете приложения под Android, то вам, скорее всего, нужно где-то хранить данные. Вы можете выбрать один из облачных сервисов (в этом случае удобно пользоваться SyncAdapter) или воспользоваться встроенной SQLite-базой данных. Во втором случае вам придётся выбрать между записью SQL-запросов с помощью Content Provider (если вы планируете использовать данные несколькими приложениями) или ORM.

В этом посте мы рассмотрим некоторые из Android ORM, которые вы можете использовать в своих приложениях.

OrmLite


Пожалуй, это первый ORM, который приходит в голову. Однако это не Android ORM, а Java ORM с поддержкой SQL-баз. Его можно использовать везде, где применяется Java, например, в JDBC-соединениях, Spring и — Android.
Здесь активно используются аннотации, например, @DatabaseTable для каждого класса, определяющего таблицу, а @DatabaseField — для каждого поля в классе.
Простой пример использования OrmLite для определения таблицы:

@DatabaseTable(tableName = "users")
public class User {
    @DatabaseField(id = true)
    private String username;
    @DatabaseField
    private String password;
 
    public User() {
        // ORMLite needs a no-arg constructor
    }
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
 
    // Implementing getter and setter methods
    public String getUserame() {
        return this.username;
    }
    public void setName(String username) {
        this.username = username;
    }
    public String getPassword() {
        return this.password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}


Это open source-проект, вы можете найти его на GitHub. За более подробной информацией можно обратиться к официальной документации.

SugarORM


Этот ORM создавался именно для Android. В комплекте поставляется API, который легко изучить и запомнить. Он может сам создавать необходимые таблицы и содержит простые методы формирования связей по типу «один-с-одним» и «один-со-многими». Также SugarORM облегчает операции создания, чтения, обновления и удаления (CRUD) с помощью всего лишь трёх функций: save(), delete() и find() (или findById()).
Чтобы использовать SugarORM в вашем приложении, нужно в AndroidManifest.xml добавить четыре тэга meta-data:

<meta-data android:name="DATABASE" android:value="my_database.db" />
<meta-data android:name="VERSION" android:value="1" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.my-domain" />


Теперь можно использовать этот ORM в классах, которые вы хотите превратить в таблицы:

public class User extends SugarRecord<User> {
    String username;
    String password;
    int age;
    @Ignore
    String bio; //this will be ignored by SugarORM
 
    public User() { }
 
    public User(String username, String password,int age){
        this.username = username;
        this.password = password;
        this.age = age;
    }
}


Добавление нового пользователя:

User johndoe = new User(getContext(),"john.doe","secret",19);
johndoe.save(); //stores the new user into the database


Удаление всех пользователей в возрасте 19 лет:

List<User> nineteens = User.find(User.class,"age = ?",new int[]{19});
foreach(user in nineteens) {
    user.delete();
}


Подробнее изучить возможности SugarORM можно в его документации.

GreenDAO


Если вам необходима высокая производительность, то обязательно присмотритесь к GreenDAO. Как сказано на их сайте, «большинство сущностей может быть добавлено, обновлено или загружено с производительностью несколько тысяч операций в секунду». И если бы авторы лукавили относительно возможностей своего детища, то оно вряд ли было бы использовано в этих всем известных приложениях. По сравнению с тем же OrmLite, GreenDAO почти в 4,5 раза быстрее:



По размеру он меньше всего на 100 Кб, что не имеет особого значения.
Посмотреть примеры использования GreenDAO можно в туториале, использующем Android Studio. Также желающие могут ознакомиться с кодом проекта на GitHub и изучить документацию.

ActiveAndroid


Как и многие другие ORM, ActiveAndroid помогает хранить и извлекать записи из SQLite без создания SQL-запросов.

Для использования ActiveAndroid нужно добавить jar-файл в папку /libs. Как сказано в начальной инструкции, нужно скопировать исходный код с GitHub и скомпилировать с помощью Maven. После подключения ActiveAndroid к вашему проекту, добавьте в AndroidManifest.xml тэги meta-data:

<meta-data android:name="AA_DB_NAME" android:value="my_database.db" />
<meta-data android:name="AA_DB_VERSION" android:value="1" />


После этого можно при необходимости вызывать ActiveAndroid.initialize():

public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActiveAndroid.initialize(this);
 
        //rest of the app
    }
}


Теперь вы можете, используя аннотации, создавать модели как Java-классы:

@Table(name = "User")
public class User extends Model {
    @Column(name = "username")
    public String username;
 
    @Column(name = "password")
    public String password;
 
    public User() {
        super();
    }
 
    public User(String username,String password) {
        super();
        this.username = username;
        this.password = password;
    }
}


Это простой пример использования ActiveAndroid, для более сложных способов применения изучите документацию проекта.

Realm


Последний ORM в нашем обзоре, Realm написан на С++ и запускается прямо на вашем устройстве (без интерпретации), что обеспечивает очень высокую производительность. Код версии для iOS, если это кому интересно, можно найти на GitHub. Также на офсайте вы можете найти примеры использования Realm на языках Objective-C и Swift.

Заключение


Конечно, это далеко не единственные ORM, существующие в природе. За рамками данного обзор остались, например, Androrm и ORMDroid. Несомненно, каждый разработчик должен уметь работать с SQL, но создание запросов — занятие скучное и ленивое, так почему бы не использовать для автоматизации процесса один из множества готовых ORM? Они могут существенно упростить вашу работу.
Yota Devices
44.30
Company
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 38

    0
    В ActiveAndroid, как я погляжу, можно на уровне аннотаций задать имя поля, тогда можно использовать нормальные наименования для свойств класса, а имя поля задавать аннотациями.

    А в других нельзя так? В OrmLite, например, если я на свойство name класса поставил DatabaseField, то в таблице необходимо, чтобы поле называлось именно так?

    p.s. а вы сами используете какие-то ORM? Если бы речь шла о каком-то серверном приложении, то я бы не спрашивал, на клиенте просто редко сталкиваешься с ситуации, когда тонны данных записываются/читаются в SQLite.
      +2
      В OrmLite можно. @DatabaseField(columnName="name") А так же множество других параметров — generatedId, index, unique, canBeNull, defaultValue, и т.д. и т.п.
      +3
      Было бы интересно увидеть именно сравнение этих библиотек. Наличие/отсутствие каких-либо функций, по которым уже можно делать свой выбор. В частности для меня было критично то, что, например, ORMLite не умеет работать через ContentProvider. Планирую как-нибудь попробовать Сupboard, там, говорят, есть возможность работать через провайдер.
        0
        Когда появилась необходимость синхронизировать базу с облаком, сделал такую возможность в UCAOrm.
          0
          Для ormlite есть возможность использовать ContentProvider. Например github.com/jakenjarvis/Android-OrmLiteContentProvider
          +4
          Сейчас будет имхо: полноценный ORM на мобильных устройствах — оверхед как по памяти, так и по производительности.

          Как правило, достаточно просто библиотеки, которая обеспечивает вам уход от прямой работы с неудобным API ContentProvider и/или SQLiteDatabase.

          И такие библиотеки есть:


          BambooStorage мейтенится мной, используется в продакшене в нескольких приложениях, полет нормальный, есть мощные планы по 2.0 (постоение запросов через билдеры, поддержка SQLiteDatabase без ContentProvider, поддержка RxJava, compile-time генерация методов для парсинга/сериализации).

          На данный момент оно предоставляет Collection-like API для хранения ваших «классов» в ContentProvider, при этом оверхед по производительности сведен к минимуму, по памяти — оверхеда вообще нет
            0
            storIOSQLite
              .get()
              .listOfObjects(Tweet.class)
              .withQuery(Tweet.ALL_TWEETS_QUERY)
              .withMapFunc(Tweet.MAP_FROM_CURSOR)
              .prepare()
              .createObservableStream() // here is the magic! It will be subscribed to changes in tables from Query
              .subscribeOn(Schedulers.io())
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe(new Action1<List<Tweet>>() {
                @Override public void call(List<Tweet> tweets) {
                  // display the data
                  // magic: this will be called on each update in "tweets" table
                }
              });
            

            Это конечно круто. Как понимаю внутри Callable interface?
            А что на счет добавления группы записей?
            В ContentProvider можно использовать bulkInsert для этого
             @Override
                public int bulkInsert(Uri uri, ContentValues[] values) {
                ...
            

            Как у вас организовано?
            Хорошо бы пример использования такого случая…
              0
              Тут такое дело:

              1 — BambooStorage это теперь StorIO, 100% новый код, новый подход, релиз 1.0.0 уже скоро
              2 — StorIO предоставляет API для работы как с SQLiteDatabase, так и с ContentResolver

              Как понимаю внутри Callable interface?
              не понял о чём конкретно вопрос :)

              А что на счет добавления группы записей?
              В ContentProvider можно использовать bulkInsert для этого


              В StorIO есть три операции: Get, Put, Delete. На данный момент BulkInsert для ContentResolver к сожалению в такой подход вписывается плохо, т.к. гарантий, что конфликтов при записи в контент провайдер нет, перед вставкой, каждая запись проверяется: нет — insert, есть — update. Так что ответ пока такой — bulkInsert не поддерживается, возможно мы что-то предложим в будущем.
                0
                1000 записей добавить, через транзакцию делать самому?

                dataBase.beginTransaction();
                ...
                dataBase.insert
                ...
                dataBase.setTransactionSuccessful();
                dataBase.endTransaction();
                


                И что будет с ContentObserver на курсоре. Именно bulkInsert решал проблему дергания 1000 раз в приведенном примере 1000 запросов

                Встретил в ваших примерах знакомую конструкцию)
                Подумал внутри что то подобное…

                    public class DoTask implements Callable<Object> {
                        public Object call() {
                            return retDo;
                        }
                    }
                

                  0
                  StorIOSQLite конечно же поддерживает транзакции, но вот в случае с ContentProvider — вы не можете через ContentResolver начать транзакцию и вызывать insert, update, delete.

                  BulkInsert в ContentResolver, на мой взгляд, — хак, т.к. по сути его можно использовать только для своего контент провайдера, когда вы точно знаете что он пустой или что там таких записей нет и все значения успешно заинсертятся. Как правило, bulkInsert применяется для первоначального наполнения контент провайдера, но в таком случае это ваш контент провайдер и можно напрямую записать через транзакцию в SQLite если, конечно, в контент провайдере используется SQLite.

                  Когда мы обращаемся в чужой контент провайдер, у нас нет гарантий, что там нет таких же данных, bulkInsert в таком случае опасен.
                    0
                    Как правило, bulkInsert применяется для первоначального наполнения контент провайдера

                    Да именно это и имел в виду. Иногда надо небольшими пачками дописывать. Использовал действительно только в одном месте и был уверен
                    Даже скажу применил
                    long id = db.insertWithOnConflict(TABLE_NAME, null, value, SQLiteDatabase.CONFLICT_REPLACE);
                    

                    Все же думаю попробовать UcaOrm
                    Подкупило описание на хабре из трех статей стэб бай стэп так сказать. Описано именно то что требовалось в проекте, а также сверх.
                    MIT лицензия!

                      0
                      вот в случае с ContentProvider — вы не можете через ContentResolver начать транзакцию и вызывать insert, update, delete.
                      Вы можете реализовать ContentProvider.applyBatch(ArrayList<ContentProviderOperation> operations), список operations — это ваши insert/update/delete, полученные с помощью ContentProviderOperation.new*(). В реализации поддерживайте транзакции и не вызывайте notifyChange когда не надо.
                        0
                        Да, можно реализовать applyBatch, это подойдёт для удаления множества записей, но Put Operation требует проверки на конфликт, соответственно, сначала нужно проверить есть ли такая запись, а затем сделать insert или update, StorIOContentResolver будет использовать applyBatch там, где это возможно
                          0
                          Проверка на конфликт не всегда нужна. Если вы используете ContentProvider поверх SQLite, то можно использовать
                          db.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE), и это будет то же самое, только все проверки будет делать за вас SQLite. Всё, конечно, зависит от конкретного случая.
                            0
                            Да, согласен. Но insertWithOnConflict не всегда подходит, к сожалению. В StorIO можно определить свое поведение для каждого типа операций и использовать insertWithOnConflict, вместо query -> insert/update, например.
                              0
                              Я не сравниваю их, я оба ответа выше по треду просто говорил, что при использовании ContentProvider'a можно сделать так или этак.
              0
              public User() { }
              public User(String username, String password,int age){ ... }
              User johndoe = new User(getContext(),"john.doe","secret",19);
              Зачем юзеру контекст?
                +3
                Sugar вообще откровенно так себе написан: синглтон, SugarContext.init(context) приведет к мемори ликам, если передать туда не ApplicationContext и все такое. Разработчик еще пишет, что в Application.onTerminate() (который никогда не вызывается) нужно вызывать SugarContext.terminate(), более менее опытный человек бы такого не понаписал, конечно
                  0
                  А вы не поясните про мемори лики в синглтоне?
                    0
                    Конкретно в Sugar вызов SugarContext.init(context) с передачей в качестве контекста, например, Activity, вы получите постоянно удерживаемую сильную ссылку на эту Activity, соответственно, GC не сможет освободить память, связанную с данной Activity, пока есть хоть одна сильная ссылка на нее.

                    Правильным решением, со стороны Sugar, было бы вызывать context.getApplicationContext() при сохранении ссылки в SugarContext, к тому же, это бы дало runtime проверку на передачу null в качестве контекста.
                      0
                      Спасибо большое
                        0
                        Если в библиотеке написано
                        context.getApplicationContext()
                        все будет ок.
                          0
                          Собственно, я об этом и написал, в SugarContext.init(context) ссылка на context сохраняется как есть
                            0
                            Точно. Не обратил внимания, виноват.
                    0
                    Смею предположить, что там доступ к ресурсам нужен будет…
                      0
                      Окей, но я не зря привел конструкторы класса User. Этот код не скомпилится.
                        +1
                        А слона то я и не заметил =/
                    +3
                    Realm не является ORM, он даже не использует SQLite. Это написанная с нуля NoSql база.
                    Фраза " Realm написан на С++ и запускается прямо на вашем устройстве (без интерпретации), что обеспечивает очень высокую производительность" вообще не понятна, все ORM как бы запускается на устройстве.

                    А кто-то использовал ORM вместе с Kotlin? Завести Realm не получилось из-за его особенности процессинга аннотаций и требований к классам модели.
                      0
                      Перешел на Realm, работает быстро, писать просто. Жду когда введут запросы на ссылки.
                        0
                        В 0.72 уже появились:
                        We just released Realm Java 0.72.0 to this website and to Maven. It includes support for Eclipse, Link Queries, Sorting on more field types, Transaction Rollback and more!

                        realm.io/news/
                        0
                        Почему не использовать встраиваемые объектные БД? вроде db4o. Минус один уровень абстрагирования
                          0
                          Наконец все идет к тому, что дизайн — это прежде всего удобство пользования (или если хотите — юзабилити), а не оформление и эффекты.
                            0
                            «создание запросов — занятие скучное и ленивое» — не согласен. Я даже немного скучаю по былым временам девелопинга под 1cv8, когда многие алгоритмы строятся с использованием запросов, составление которых порой превращается в хитромудрый квест, делающих до 70-80% работы алгоритма.
                              –3
                              В Android есть еще одно хранилище, очень простое — SharedPreferences
                                0
                                Это не БД, это просто XML файлы в определённой директории. Несвязный набор пар ключ-значение. При чем тут ORM?
                                –2
                                Внесу свою лепту к этой статье: я бы не советовал использовать любые ОРМ в тех случаях, если у вас огромные наборы данных и вложенные коллекции больше 2х уровней. Выборка большого количества данных ужасно медленно работает.
                                Также, как сказал товарищ выше, ORMLite не умеет работать с ContentProvider, поэтому приходится искать обходные пути.
                                  –1
                                  Перепробовав все ORM на Android я выбрал для себя следующий набор для работы с БД:
                                  1: ProviGen — для генерации CP и SQLiteOpenHelper на базе контракта.
                                  2: Cupboard — для удобного построения запросов и cursor to object преобразований.
                                    +1
                                    Не знаю как вы, но я использую в играх для мобил лучший ORM (с поддержкой MongoDB/Membase и кучей других хранилищ данных) Spring Data projects.spring.io/spring-data/

                                    1. Простотота
                                    2. Скорость
                                    3. Отсутвие велосипедов

                                    Если есть время изучать велосипеды с синтаксическим сахором, ради бога. Но я более-менее выбираю «стандарт»

                                    Only users with full accounts can post comments. Log in, please.