...

вторник, 23 июля 2013 г.

Пишем свой Orm под Android с канастой и сеньоритами, Часть 2-ая

Вступление




Некоторый интерес сообщества к моей первой статье, заставил меня усиленно поработать над orm. Мне еще не все в нем нравиться (где-то код не оптимизирован; где-то реализация не такая, как я хотел; не хватает проверок и возможно стабильности), но он выполняет все необходимые в текущей момент мне функции. И так, встречайте: UcaOrm!

Использование




Работу с orm будем рассматривать на примере следующей модели (подробнее о модели в 1-ой части):

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;
}




Настройка



После добавления библиотеки в проект (в дальнейшем я планирую выкладывать собранный jar, а пока вы можете самостоятельно собрать его из исходников), необходимо создать Helper унаследованный от OrmHelper:

public class DataBaseHelper extends OrmHelper {

public DataBaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}

@Override
protected void onCreate() {
}

@Override
protected void onUpgrade(int oldVersion, int newVersion) {
}

@Override
public void getDefaultValues(Class<? extends OrmEntity> entityClass, ArrayList<String> columns, ArrayList<ContentValues> valueList) {
}
}


Метод getDefaultValues позволяет добавлять в создаваемые таблицы начальные данные. Его реализация пока не очень удобна, и в будущем подвергнется переработке.

Теперь нам надо зарегистрировать helper. Делается это через наследника Application:



public class MyApp extends Application {

@Override
public void onCreate() {
super.onCreate();
try {
OrmFactory.SetHelper(DataBaseHelper.class, getApplicationContext());
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void onTerminate() {
OrmFactory.ReleaseHelper();
super.onTerminate();
}
}


По умолчанию, имя базы формируется из Label приложения, а версия будет 1-ой. Чтобы это изменить, добавим в manifest следующие строки:



<meta-data android:name="UO_DB_NAME" android:value="Cars" />
<meta-data android:name="UO_DB_VERSION" android:value="1" />




Создание таблиц



Таблицы создаются в методе onCreate, а обновление в методе onUpdate, однако обновление еще пока не поддерживается в orm.

И так, создадим необходимые таблицы:

protected void onCreate() {
try {
OrmUtils.CreateTable(CarType.class);
OrmUtils.CreateTable(Car.class);
OrmUtils.CreateTable(Wheel.class);
OrmUtils.CreateTable(Truck.class);
} catch (Exception e) {
e.printStackTrace();
}
}




И заполним таблицу CarType начальными данными:

public void getDefaultValues(Class<? extends OrmEntity> entityClass, ArrayList<String> columns, ArrayList<ContentValues> valueList) {
ContentValues values;
if (entityClass.equals(CarType.class)) {
values = new ContentValues();
values.put(columns.get(0), "Passenger");
valueList.add(values);
values = new ContentValues();
values.put(columns.get(0), "Truck");
valueList.add(values);
}
}




Как уже говорил, реализация getDefaultValues хромает.
Простая выборка



Так как мы уже наполнили таблицу CarType данными, то предлагаю сначала рассмотреть простую выборку, а потом уже создание экземпляров и выборку с Where.

Давайте создадим экземпляр типа «Легковой автомобиль». Для этого нам понадобиться сначала получить все доступные нам типы. Поскольку все сущности, с которыми работает orm должны наследоваться от OrmEntity, воспользуемся его статическим методам getAllEntities. Для красоты, мы можем перекрыть его в классе CarType:

public static List<CarType> getAllCarTypes(){
try {
return OrmEntity.getAllEntities(CarType.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}




Создание сущностей



Создам экземпляр Car:

Car car = new Car();
car.setType(CarType. getAllCarTypes().get(0));
car.setEnginePower(116);
car.setDoorsCount(4);
Wheel whell = new Whell();
whell.setCar(car);
whell.setManufacturer("Michrelli");
car.addWheel(whell);




Конечно whell.setCar лучше дернуть внутри addWheel как whell.setCar(this), но приведу его здесь для наглядности.

И просто дергаем метод alter:

car.alter();




Немного изменим экземпляр:

car.setEnginePower(120);
car.getWheels().get(0).setManufacturer("Pirlin");


И снова просто вызываем, alter:



car.alter();




Теперь создадим «Грузовик»:

Truck truck = new Truck();
truck.setType(CarType.getAllCarTypes().get(1));
truck.setEnginePower(220);
truck.setDoorsCount(2);
Wheel whell = new Whell();
whell.setCar(truck);
whell.setManufacturer("Michrelli");
truck.addWheel(whell);
truck.setTipper(true);
truck.alter();




Причем нам не важен тип переменной, мы можем объявить её и как Car, главное, что в ней лежит.
Выборка



Реализуем в Car два метода:

public static List<Car> getAllCars() {
try {
return OrmEntity.getAllEntities(CarType.class, true);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

public static List<Car> getCarsWithoutTrucks() {
try {
return OrmEntity.getAllEntities(CarType.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}




Разница лишь в вызове getAllEntities, однако getAllCars вернет две записи, а getCarsWithoutTrucks только одну: второй параметр указывает, выбирать ли записи связанные с дочерними таблицами, или же только самодостаточные.

Важно, заметить следующее: допустим мы создадим второй экземпляр с типом Passenger. При выборке, так как класс CarType помечен как CashedList, оба экземпляра Car будут ссылаться на один и тот же экземпляр CarType.
Выборка с Where



Ну, и самое интересное!

В OrmEntity есть статический метод Where который принимает класс сущности для выборки. Для красоты перекроем его в Car:

public static OrmWhere Where(){
return Where(Car.class);
}




И попытаемся найти машины с мощностью 120 л.с:

List<Car> cars = Car.Where().Equels("engine_power", 120).Select();




Или машины с мощностью 120 л.с. и у которых четыре двери:

List<Car> cars = Car.Where().Equels(“engine_power”, 120).And().Equels("doors_count", 4).Select();




Или одну из машин у которой есть колесо от Pirlin:

Car car = Car.Where().FindChild(Wheel.class, new OrmWhere(Wheel.class).Equels("manufacturer", "Pirlin")).SelectFirst();




Выборку с включение из дочерних таблиц пока сделать нельзя (хотя orm её поддерживает и, все что надо, это добавить еще один метод Select с параметром includeLeftChild в OrmWhere).

Заключение




Вот почти и все на что пока способен мой orm. Сейчас, номер версии, что я ему присвоил бы – 0.1. Как я уже говорил, он реализует все, что нужно моему приложению в данный момент, и поэтому, такие мелочи, как например, Less или Great в OrmWhere – пока не реализованы. Не реализована возможность обновления или удаления таблиц (хотя можно добавлять новые через тот же CreateTable). Из-за left и right join таблиц пришлось отказаться от вызова query с параметрами и дергать rawQuery у SQLiteDatabase, а так же затраты на выборку всех записей кэшированных списков возросли с O(n-k) (где n – кол-во всех записей в таблице, а k – кол-во уже выбранных записей) до O(n). Поддерживаемые сейчас типы, это: Int, Long, Double, Date, String, Drawable и Document.

Возможно, что-то забыл упомянуть, но в любом случае буду рад любым вопросам и, особенно, предложениям по развитию UcaOrm.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: 'You Say What You Like, Because They Like What You Say' - http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/731-you-say-what-you-like-because-they-like-what-you-say.html


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

Отправить комментарий