Вступление
Некоторый интерес сообщества к моей первой статье, заставил меня усиленно поработать над 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.
ссылка на оригинал статьи http://habrahabr.ru/post/187526/
Добавить комментарий