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.
