Обзор лучшей AndroidORM — ActiveAndroid

ActiveAndroid — библиотека, позволяющая работать с БД без написания SQL-запросов. Библиотека может сохранять объекты в таблицу и извлекать их также в виде объектов.

Предварительная настройка

Первое, что нужно делать, если вы не сделали этого, это скачать библиотеку ActiveAndroid. Так же можно скачать последнюю стабильную версию в  jar-файле.

После добавления в проект нам нужно добавить некоторые мета-данные в файл AndroidManifest.xml, а именно:

  • AA_DB_NAME (по желанию)
  • AA_DB_VERSION (опционально — по умолчанию 1)
<manifest ...>
    <application android:name="com.activeandroid.app.Application" ...>

        ...

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

    </application>
</manifest>

Отмечу также, что атрибут name у application указывает на класс Application от ActiveDroid. Это необходимо для работы библиотеки. Если у вас есть другой класс, который наследуется от обычного Applicatin, то просто унаследуйте этот класс от com.activeandroid.app.Application.

public class MyApplication extends com.activeandroid.app.Application {

Но что делать, если ваш Application уже наследуется от другого класса, чтобы использовать другую библиотеку? Просто инициализируйте ActiveAndroid в классе Application.

public class MyApplication extends SomeLibraryApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        ActiveAndroid.initialize(this);
    }
}

В нашем примере мы имеем две таблицы: Category и Item.
Далее мы создаем классы для этих таблиц.

Вот наши классы:

@Table(name = "Categories")
public class Category extends Model { 
    @Column(name = "Name")
    public String name;
}

@Table(name = "Items")
public class Item extends Model {
    @Column(name = "Name")
    public String name;

    @Column(name = "Category")
    public Category category;
}
Создание модели базы данных

Создание таблиц в ActiveDroid происходит очень легко. Для этого всего-то нужно создать класс унаследованный от специального класса Model,  который содержит в себе специальные методы для сохранения/удаления записей. Каждое поле  в классе должно быть аннотировано при помощи аннотации @Column. ActiveAndroid может сохранять поля только с примитивными типами. О том, как сохранять собственные типы мы поговорим позже. Также, хочу отметить, что нам не нужно создавать поле id в нашем классе, библиотека сделает это автоматически.

Если вы определяете для класса собственный конструктор с параметрами, то определите еще один конструктор без параметров.

@Table(name = "Items")
public class Item extends Model {

        @Column(name = "Name")
        public String name;

        @Column(name = "Category")
        public Category category;

        public Item() {
                super();
        }

        public Item(String name, Category category) {
                super();
                this.name = name;
                this.category = category;
        }
}

Пр аннотировании поля в скобках мы указываем имя столбца в таблице. Если аннотация будет без скобок то за имя возьмется название поля.

Зависимости

Item принадлежит категории, а категории имеют много Item’ов. Как мы представим, это в ActiveAndroid?

В классе Item мы можем сделать прямое отношение к категории, в которой он находится, создав поле для Category.

@Table(name = "Items")
public class Item extends Model {

    @Column(name = "Name")
    public String name;

    @Column(name = "Category")
    public Category category;
}

Точно так же, в классе Category можете указать это отношения ко многим Item’ам:

@Table(name = "Categories")
public class Category extends Model {
    @Column(name = "Name")
    public String name;

    public List<Item> items() {
        return getMany(Item.class, "Category");
    }
}
Ускоряем работу

Для поиска моделей в вашем приложении ActiveDroid сканирует все файлы. Это может замедлить работу приложения, если у вас много зависимостей. Ускорить работу позволят метаданные, которые содержат информацию о конкретных классах моделях:

<meta-data
    android:name="AA_MODELS"
    android:value="com.myapp.model.Item, com.myapp.model.Category" />
Зарезервированные имена

Во избежание проблем при использовании библиотеки не используйте следующие имена в качестве имен столбцов:

ABORT               DEFAULT         INNER         REGEXP
ACTION              DEFERRABLE      INSERT        REINDEX
ADD                 DEFERRED        INSTEAD       RELEASE
AFTER               DELETE          INTERSECT     RENAME
ALL                 DESC            INTO          REPLACE
ALTER               DETACH          IS            RESTRICT
ANALYZE             DISTINCT        ISNULL        RIGHT
AND                 DROP            JOIN          ROLLBACK
AS                  EACH            KEY           ROW
ASC                 ELSE            LEFT          SAVEPOINT
ATTACH              END             LIKE          SELECT
AUTOINCREMENT       ESCAPE          LIMIT         SET
BEFORE              EXCEPT          MATCH         TABLE
BEGIN               EXCLUSIVE       NATURAL       TEMP
BETWEEN             EXISTS          NO            TEMPORARY
BY                  EXPLAIN         NOT           THEN
CASCADE             FAIL            NOTNULL       TO
CASE                FOR             NULL          TRANSACTION
CAST                FOREIGN         OF            TRIGGER
CHECK               FROM            OFFSET        UNION
COLLATE             FULL            ON            UNIQUE
COLUMN              GLOB            OR            UPDATE
COMMIT              GROUP           ORDER         USING
CONFLICT            HAVING          OUTER         VACUUM
CONSTRAINT          IF              PLAN          VALUES
CREATE              IGNORE          PRAGMA        VIEW
CROSS               IMMEDIATE       PRIMARY       VIRTUAL
CURRENT_DATE        IN              QUERY         WHEN
CURRENT_TIME        INDEX           RAISE         WHERE
CURRENT_TIMESTAMP   INDEXED         RECURSIVE     WITH
DATABASE            INITIALLY       REFERENCES    WITHOUT
                    ID
Предварительно заполненная БД

Вы можете использовать заранее заполняется базу данных, выполните следующие действия:

1. Поместите копию файла базы данных SQLite в assets каталоге вашего приложения, например:

/myapp/src/main/assets/prepop.db

2. Убедитесь, что вы прописали AA_DB_NAME в файле манифеста:

<meta-data android:name="AA_DB_NAME" android:value="prepop.db" />

После установки приложение скопирует файл prepop.db из папки assets в папку /data/data/myapp/databases

Обратите  внимание, что если вы не определили столбец id в вашей модели, то в таблице столбец с id будет называться Id, а не _id, который является стандартным в БД Android. Чтобы не допустить этого в аннотации @Table в скобках добавьте id = «_id»:

@Table(name = "Items", id = "_id")
public class Item extends Model {
}
Сохранение в БД

Теперь у вас есть ваши настроенные модели базы данных, давайте сохраним несколько записей в таблицу.

Чтобы сохранить запись в таблицу создайте экземпляр класса вашей модели и вызовите у него метод save(). Этот метод вставляет и обновляет записи в БД:

Category restaurants = new Category();
restaurants.name = "Restaurants";
restaurants.save();

Давайте создадим экземпляр класса Item и свяжем его с категорией:

Item item = new Item();
item.category = restaurants;
item.name = "Outback Steakhouse";
item.save();

Добавим больше элементов:

item = new Item();
item.category = restaurants;
item.name = "Red Robin";
item.save();

item = new Item();
item.category = restaurants;
item.name = "Olive Garden";
item.save();
Массовая вставка (транзакция)

Для одновременной вставки большого количества записей в таблицу используйте транзакции. Использование транзакций использование ускоряет вставку примерно в 100 раз:

ActiveAndroid.beginTransaction();
try {
        for (int i = 0; i < 100; i++) {
            Item item = new Item();
            item.name = "Example " + i;
            item.save();
        }
        ActiveAndroid.setTransactionSuccessful();
} finally {
        ActiveAndroid.endTransaction();
}
Читаем записи

Все запросы в ActiveAndroid используют синтаксис Builder’а, или метод Model.query ().

У нас есть модель Item:

@Table(name = "Items")
public class Item extends Model {
    @Column(name = "Name")
    public String name;

    @Column(name = "Category")
    public Category category;
}

Было бы неплохо получать случайную запись из таблицы. Давайте добавим в нашу модель метод, чтобы сделать это:

public static Item getRandom() {
    return new Select().from(Item.class).orderBy("RANDOM()").executeSingle();
}

Построение запросов в ActiveAndroi происходит так же, как и в SQL. Мы создаем экземпляр класса Select и в методе from передаем ему наш класс.Затем мы используем метод orderBy для выбора записей в случайном порядке. Для выполнения запроса мы вызываем метод execute() или executeSingle(). Разница в том, что метод execute() возвращает List с объектами, а метод executeSingle() возвращает одну запись.

Если мы хотим получить элемент определенной категории нам нужно вызвать метод where у Builder’а и передать ему наши аргументы:

public static Item getRandom(Category category) {
    return new Select()
        .from(Item.class)
        .where("Category = ?", category.getId())
        .orderBy("RANDOM()")
        .executeSingle();
}

Также мы можем получить элементы определенной категории, отсортированный по именам:

public static List<Item> getAll(Category category) {
    return new Select()
        .from(Item.class)
        .where("Category = ?", category.getId())
        .orderBy("Name ASC")
        .execute();
}
Удаление записей

Для того, чтобы удалить запись нужно вызвать метод delete(). В следующем примере мы загружаем запись по id и удаляем ее:

Item item = Item.load(Item.class, 1);
item.delete();

Можно удалить запись так:

Item.delete(Item.class, 1);

Вы также можете удалять записи, используя синтаксис Builder’а:

new Delete().from(Item.class).where("Id = ?", 1).execute();
Сериализаторы для собственных типов

ActiveAndroid обрабатывает множество типов по умолчанию. Если Вам вам нужно для обработать пользовательский тип данных, вы легко можете сделать это, используя TypeSerializer. Лучший способ узнать, как он работает,это посмотреть на пример.Давайте рассмотрим тип Date.

При создании TypeSerializer мы должны подумать, как мы можем преобразовать наш тип до примитивного типа, с которым ActiveAndroid может справиться. Даты могут быть преобразованы в Long, поэтому давайте используем этот тип.

Вот код, используемый для преобразования дат:

final public class UtilDateSerializer extends TypeSerializer {
    @Override
    public Class<?> getDeserializedType() {
        return Date.class;
    }

    @Override
    public Class<?> getSerializedType() {
        return Long.class;
    }

    @Override
    public Long serialize(Object data) {
        if (data == null) {
            return null;
        }

        return ((Date) data).getTime();
    }

    @Override
    public Date deserialize(Object data) {
        if (data == null) {
            return null;
        }

        return new Date((Long) data);
    }
}

В методе getDeserializedType мы возвращаем тот тип, который мы хотим сериализовать. Для класса Date в TypeSerializer мы возвращаем Date.class. Второй метод возвращает сериализованный тип. То есть тот тип, который мы хотим сохранить в базе данных. В методах serialize и deserialize мы преобразуем наши данные.

Теперь нужно зарегистрировать наш сериализатор в Manifest.xml:

<meta-data android:name="AA_SERIALIZERS" 
      android:value="my.package.CustomTypeSerializer,my.package.AnotherCustomeTypeSerializer" />

Ссылка на библиотеку: ActiveAndroid.

Комментарии:

10 комментариев

  1. Уведомление: Использование Retrofit с ActiveAndroid | Java-Help

  2. Михаил Ответить

    Не совсем понятно почему автор статьи считает данную ОРМ лучшей. У нее много проблем. И я бы не рекомендовал ее использовать.

    • Admin АвторОтветить

      Возможно, на тот момент я посчитал ее лучшей. Сейчас я не берусь ответить за это

      • Александр Ответить

        Какие проблеммы есть у данной ORM? Что вы можете порекомендовать исходя из текущего опыта?

        • Admin АвторОтветить

          Одна из главных проблем, из-за которой отказался от использования данной библиотеки — невозможность автоматического тестирования. Для себя я нашел новую ORM — squidb.

      • Андрей Ответить

        Уважаемый админ. У меня проблема. На версиях 5 и выше почему то не работает, выдает ошибку java.lang.NoClassDefFoundError. На версия ниже все работает отлично. Можете, пожалуйста, направить в правильное русло и сказать что может быть не так и как исправить?

        • Admin АвторОтветить

          Советую посмотреть в сторону этой библиотеки. ActiveAndroid уже устарела и не соответствует современным требованиям.

          • Admin Автор

            Довольно долго искал что-нибудь похожее на ActiveAndroid и нашел — DBFlow.

  3. Уведомление: Используем ORM DBFlow в своем приложении | Java-Help

  4. Уведомление: База данных Android SQLite с использованием Sugar ORM | Java-Help

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *