📜 ⬆️ ⬇️

We write our Orm for Android with Canas and Senorites

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 .
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.

GreenDAO

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.

ActiveAndroid

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


  1. No entity generators! All through annotations.
  2. As few as possible different types of annotations, ideally two (as long as you manage to adhere to this).
  3. Query entities through static methods of the class itself.
  4. Creating, saving and deleting is through instance methods.
  5. The most simple Helper with the ability to create initial records of tables.


Implementation


Annotations

I have only two abstracts:
  1. 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.
  2. 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 .

Source: https://habr.com/ru/post/186078/


All Articles