Introduction
The idea to write my application for
Android came to me on the fifth day of rest in sunny Thailand. I will not go into the details of what exactly prompted me to it, how and what I planned for the application (just an article is not about that). However, the idea was firmly rooted, and on the sixth day of my stay, using the free internet at the hotel, to a laptop, taken just for the sake of watching movies and throwing off photos from the camera, I downloaded
MySql .
I started, as you probably guessed, with the relational model.
The work was difficult, but after a couple of months with the model I finished and plunged into the jungle of development for
Android . Before that, I wrote for mobile platforms only on the
.Net Compact Framework , but since I was familiar with
Java firsthand, it wasn’t difficult to distribute a simple form with buttons. The object model, as expected, did not cause any difficulties at all and I, happily anticipating how my test data will now fly away to the depths of the device, opened the
Data Storage section on the
Android Developers website. The
Using Databases section cannot be called exhaustive, but it contains all the links to the
API it contains, and I began to write my successor from
SQLiteOpenHelper . After a couple of successful trials, spoiled by the
Entity Framework , I realized that it would have been nice to use some kind of
orm , since I had more than a dozen entities. Having driven in the
Great and Horrible «
android orm », I received the first link to
this article, and several useful ones on
StackOverflow . Having collected a total of three
orm 'a, I proceeded to the experiments.
Experiments
The first experimental was of course
OrmLite .
In principle, a good implementation, the use of annotations. I also liked the implementation of
onCreate and
onUpgrade in
DatabaseHelper and it was remembered somewhere in the depths of my memory. But! To create for each entity also an additional class
<% EntityName%> DAO - thank you! You can end up trying to make one attached to the base class of entities, but this will only cause more problems.
I did not read the heading "
Data model and code generation " in the documentation. Maybe of course this approach is not completely clear to me and it has its advantages, but, having got used to the attributes, I like to use annotations more.
I had high hopes for this
orm : annotations, creating a base using manifest (remembered in the same depths as
Helper OrmLite ), mapping through the methods of the entity itself ... In general, everything seems to be good for this
framework , but it doesn’t pull my relational model smog.
')
Relational model
A little retreat from
orm , to finally explain what
kind of relational model I did. In principle, everything in it is of course standard, except for one thing. In the
Entity Framework , when mapping inherited entities, one table is created with an excess of fields. For example: suppose we have the entity “
Machine ”
public class Car { private CarType type; private int enginePower; private int doorsCount; }
And the entity "
Truck " inherited from it
public class Truck extends Car{ private boolean isTipper; }
Usually, orm should generate such a table creation request:
CREATE TABLE car (id INTEGER PRIMARY KEY, type INTEGER REFERENCE car_type(id), engine_power INTEGER, doors_count INTEGER, is_tipper INTEGER);
I, at one time impressed by the inheritance of tables in
PostgreSQL , made the following model:
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);
At once I’ll clarify that this is just an example, and in my entities, the fields and connections are much more, and therefore I didn’t want to score the
Car table, because I don’t want to refer to it.
As a result, the selection must be done via
Left Join .
As a result, I decided to invent a bicycle, that is, to write my own
Orm .
Concept
- No entity generators! All through annotations.
- As few as possible different types of annotations, ideally two (as long as you manage to adhere to this).
- Query entities through static methods of the class itself.
- Creating, saving and deleting is through instance methods.
- The most simple Helper with the ability to create initial records of tables.
Implementation
Annotations
I have only two abstracts:
- Table - applied to the class, describes the table. Properties:
- Name - the name of the table, if empty - then the name of the class is taken in lower case.
- CashedList - indicates whether it is necessary to load each one anew for instances of this class, or to search for it in the cached list (I will explain later).
- LeftJoinTo - indicates the class to which the given is the successor.
- RightJoinTo - indicates the classes that extend the current.
- Column - applied to the field, describes the columns of the table. Properties:
- Name - the name of the column, if empty, then the field name is taken in lower case.
- PrimaryKey - indicates that this field is key.
- Inherited indicates that the annotation is inherited.
- ReferenceActions - a list of actions for the References field.
Entities
Again, we will consider them from the example:
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; }
As a result of the work of
Helper ', which is similar to
Helper OrmLite, we will receive the following queries:
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);
Advanced by orm
The
OrmEntity class has a protected static method:
protected static <T extends OrmEntity> List<T> getAllEntities (Class<T> entityClass)
For example, in the
CarType class,
we implement the following method:
public static List<CarType> getAllEntities() { return getAllEntities(CarTpe.class); }
Also in
OrmEntity there are protected methods
alter and
delete . In the base class
BaseEntity, I hide
alter , and pull it through
insert and
update , well, purely for beauty.
Checks and restrictions
An entity must have a field labeled
primaryKey . It must be of the
Long type (
L ong , so that the
id would initially be
null 'om and
alter could understand that it is an
insert or
update ).
If the field labeled
Column is of type
List with an entity class inherited from
OrmEntity , then this class must have a field of type first class labeled
Column .
If the field marked
Column has a type inherited from
OrmEntity , then this type must have a field marked
Column with a
primaryKey .
If a class is marked with a
Table with
leftJoinTo , then the class specified in
leftJoinTo should be marked with a
Table in the
rightJoinTo of which the first class is added.
All unchecked checks throw
Exception .
Conclusion
At the current moment
orm is not finished yet, and I develop it as my needs. The types
int ,
Long ,
double ,
String ,
Drawable are now supported. On the approach of the
Document .
From the actions on references, while there is only
ON DELETE , it is possible to make both
CASCADE and
SET NULL and the rest.
LeftJoinTo - while not fully working, now I finish just it.
As soon as I’ll finish all my plans and brush the code, I’ll upload the library to
GitHub .