- PVSM.RU - https://www.pvsm.ru -
Идея написать свое приложение под Android пришла мне на пятый день отдыха в солнечном Таиланде. Не буду вдаваться в подробности что именно натолкнуло меня на неё, как и что я задумал за приложение (просто статья не об этом). Однако идея крепко укоренилась и на шестой день пребывания, воспользовавшись бесплатным интернетом в отеле, на ноутбук, взятый только ради просмотра фильмов и скидывания фотографий с фотоаппарата, я закачал MySql.
Начал я, как вы наверное уже догадались, с реляционный модели.
Работа шла трудно, но через пару месяцев с моделью я закончил и окунулся в дебри разработки под Android. До этого, под мобильные платформы я писал только на .Net Compact Framework, но так как с Java был знаком не понаслышке, накидать простенькую форму с кнопками труда не составило. Объектная модель, ожидаемо, трудностей не вызвала вообще и я, радостно предвкушая как сейчас мои тестовые данные улетят куда-то в недра устройства, открыл раздел Data Storage [1] на сайте Android Developers [2]. Раздел Using Databases [3] нельзя назвать исчерпывающим, однако все необходимы ссылки на API он содержит, и я принялся писать своего наследника от SQLiteOpenHelper [4]. После пары удачных проб, разбалованный Entity Framework’ом, я понял, что и тут бы было неплохо использовать какой-нибудь orm, так как сущностей у меня набралось больше десятка. Вбив в Великом и Ужасном [5] «android orm», первую же ссылку я получил на эту [6] статью, и несколько полезных на StackOverflow. Набрав в общей сложности три orm’а, я приступил к экспериментам.
Первым подопытным стало конечно OrmLite.
В принципе неплохая реализация, использование аннотации. Еще мне понравилась реализация onCreate и onUpgrade в DatabaseHelper и она запомнилась где-то в глубинах моей памяти. Но! Создавать для каждой сущности еще и дополнительный класс <%EntityName%>DAO – увольте! Можно кончено попытаться сделать один, прицепляемый к базовому классу сущностей, но проблем от этого будет только больше.
Дальше заголовка «Data model and code generation» в документации я читать не стал. Может быть конечно такой подход мне не совсем понятен и в нем есть свои плюсы, но, привыкнув к атрибутам, использовать аннотации мне нравиться больше.
На данный orm я возлагал большие надежды: аннотации, создание базы с использование manifest’а (запомнилось в тех же глубинах что и Helper OrmLite), маппинг через методы самой сущности… В общем вроде всем данный framework хорош, но потянуть мою реляционную модель он не смог.
Немного отступлю от orm, чтобы наконец объяснить, что за реляционная модель у меня получилась. В принципе, все в ней конечно стандартно, за исключением одной вещи. В Entity Framework’е, при маппинги наследуемых сущностей создается одна таблица с избытком полей. Например: предположим у нас есть сущность «Машина»
/**
* Машина
*/
public class Car {
/**
* Тип
*/
private CarType type;
/**
* Мощность двигшателя
*/
private int enginePower;
/**
* Кол-во дверей
*/
private int doorsCount;
}
И наследуемая от неё сущность «Грузовик»
/**
*Грузовик
*/
public class Truck extends Car{
/**
*Указвает на то, что это самосвал.
*/
private boolean isTipper;
}
Обычно orm должен сгенерировать такой запрос на создание таблицы:
CREATE TABLE car (id INTEGER PRIMARY KEY, type INTEGER REFERENCE car_type(id), engine_power INTEGER, doors_count INTEGER, is_tipper INTEGER);
Я же, в свое время впечатленный наследованием таблиц в PostgreSQL, сделал такую модель:
CREATE TABLE car (id INTEGER PRIMARY KEY, type INTEGER REFERENCES car_type(id), engine_power INTEGER, doors_count INTEGER);
CREATE TABLE truck (id INTEGER REFERENCES car(id) ON DELETE CASCADE, is_tipper INTEGER);
Сразу уточню, что это только пример, и в моих сущностях, полей и связей намного больше, а посему забивать таблицу Car, тем, что к ней не относиться – я не хотел.
В результате выборку надо делать через Left Join.
В результате, я решил изобрести велосипед, сиречь написать свой собственный Orm.
Аннотация у меня всего две:
Опять же будем рассматривать их из примера:
public class BaseEntity extends OrmEntity {
@Column(primaryKey = true, inherited = true)
private Long id;
}
@Table(name = “car_type”, cashedList = true)
public class CarType extends BaseEntity {
@Column
private String code;
}
@Table(rightJoinTo = {Truck.class})
public class Car extends BaseEntity {
@Column(name = “car_type”)
private CarType type;
@Column
private List<Wheel> wheels;
@Column(name = “engine_power”)
private int enginePower;
@Column(name = “doors_count”)
private int doorsCount;
}
@Table
public class Wheel extends BaseEntity {
@Column(name = “car_id”)
private Car car;
@Column
private String manufacturer;
}
@Table(leftJoinTo = Car.class)
public class Truck extends Car {
@Column(name = “is_tipper”)
private boolean isTipper;
}
В результате работы Helper’а похожего на Helper OrmLite получим следующие запросы:
CREATE TABLE car_type (id INTEGER PRIMARY KEY, code TEXT);
CREATE TABLE car (id INTEGER PRIMARY KEY, car_type REFERENCES car_type (id), engine_power INTEGER, doors_count INTEGER);
CREATE TABLE wheel (id INTEGER PRIMARY KEY, car_id INTEGER REFERENCES car (id), manufacturer TEXT);
CREATE TABLE truck (car_id REFERENCES car (id), is_tipper INTEGER);
Класс OrmEntity имеет защищенный статический метод:
protected <T extends OrmEntity> List<T> getAllEntities (Class<T> entityClass)
Например, в классе CarType реализуем такой метод:
public List<CarType> getAllEntities() {
return getAllEntities(CarTpe.class);
}
Так же в OrmEntity есть защищенные методы alter и delete. В базовом классе BaseEntity я прячу alter, и дергаю его через insert и update, ну так, чисто для красоты.
У сущности обязано быть поле помеченное primaryKey. Оно должно быть обязательно типа Long (именно Long, что бы изначально id было null’ом и alter смог понять, что это, insert или update).
Если поле помеченное Column имеет тип List с классом сущностей унаследованных от OrmEntity, то в этом классе обязательно должно быть поле типа первого класса помеченное Column.
Если поле помеченное Column имеет тип унаследованный от OrmEntity, то у этого типа обязано быть поле помеченное Column с primaryKey.
Если класс помечен Table с leftJoinTo, то у класс указанный в leftJoinTo, должен быть помечен Table в rightJoinTo которого добавлен первый класс.
Все не пройденные проверки бросают Exception.
В текущей момент orm еще не закончен, и я развиваю его по мере моих надобностей. Сейчас поддерживаются типы int, Long, double, String, Drawable. На подходе Document.
Из действия по references пока есть только ON DELETE по нему можно сделать и CASCADE и SET NULL и остальные.
LeftJoinTo – пока работает не полностью, сейчас доделываю как раз его.
Как только доделаю все задуманное и причешу код – выложу библиотеку на GitHub.
Автор: Scogun
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/38330
Ссылки в тексте:
[1] Data Storage: http://developer.android.com/intl/ru/guide/topics/data/data-storage.html
[2] Android Developers: http://developer.android.com/intl/ru/guide/components/index.html
[3] Using Databases: http://developer.android.com/intl/ru/guide/topics/data/data-storage.html#db
[4] SQLiteOpenHelper: http://developer.android.com/intl/ru/reference/android/database/sqlite/SQLiteOpenHelper.html
[5] Великом и Ужасном: http://google.ru
[6] эту: http://habrahabr.ru/post/143431/
[7] OrmLite: http://ormlite.com/
[8] GreenDAO: http://greendao-orm.com/
[9] ActiveAndroid: http://www.activeandroid.com/
[10] Источник: http://habrahabr.ru/post/186078/
Нажмите здесь для печати.