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.