Back to Home

We write our Orm for Android with canasta and senorites

android · orm · ormlite · greendao · activeandroid · java orm · own project · own experience

We write our Orm for Android with canasta 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 details of what exactly prompted me to it, how and what I planned for the application (just the 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, I downloaded MySql onto a laptop, taken just for watching movies and taking photos off the camera .
I started, you guessed it, with a relational model.
The work went hard, but after a couple of months with the model I finished and plunged into the jungle of development for Android . Before that, for mobile platforms, I wrote only on the .Net Compact Framework , but since with Javawas familiar firsthand; throwing a simple form with buttons was not difficult. The object model, as expected, did not cause any difficulties at all, and I, joyfully anticipating how my test data would fly away somewhere in the bowels of the device, opened the Data Storage section on the Android Developers website . The section Using Databases cannot be called exhaustive, however, it contains all the API references, and I started writing my SQLiteOpenHelper heir . After a couple of successful tests, spoiled by the Entity Framework , I realized that it would be nice to use some kind of orm as well, since I had more than a dozen entities. Driving in the Great and TerribleAndroid orm ”, the first link I got to this article, and some useful ones on StackOverflow . Having typed a total of three orm , I set about experimenting.

The experiments


The first test was of course OrmLite .
Ormite

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 finally try to make one that is attached to the base class of entities, but there will only be more problems from this.

Greendao

I did not read further the heading “ Data model and code generation ” in the documentation. Maybe of course this approach is not entirely clear to me and it has its advantages, but, getting used to the attributes, I like to use annotations more.

ActiveAndroid

I had high hopes for this orm : annotations, creating a database using manifest (remembered at the same depths as Helper OrmLite ), mapping through the methods of the entity itself ... In general, this framework seems to be good for everyone , but it doesn’t pull my relational model smog.

Relational model


I 'll digress a little from orm to finally explain what kind of relational model I have. In principle, everything in it is of course standard, with the exception of one thing. In the Entity Framework , with the mapping of 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 “ Truck " entity inherited from it
/**
*Грузовик
*/
public class Truck extends Car{
    /**
     *Указвает на то, что это самосвал.
     */
    private boolean isTipper;
}

Normally, orm should generate this query to create the table:
CREATE TABLE car (id INTEGER PRIMARY KEY, type INTEGER REFERENCE car_type(id), engine_power INTEGER, doors_count INTEGER, is_tipper INTEGER);

I, once impressed by the inheritance of tables in PostgreSQL , made this 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);

I’ll clarify right away that this is just an example, and in my entities, there are much more fields and relationships, and therefore I did not want to clog the Car table with the fact that I didn’t belong to it.
As a result, the selection must be done through Left Join .
As a result, I decided to invent a bicycle, in other words, write my own Orm .

Concept


  1. No entity generators! All through annotations.
  2. There are as few different types of annotations as possible, ideally two (so far this has been adhered to).
  3. Request entities via static methods of the class itself.
  4. Create, save, and delete through instance methods.
  5. The most simple Helper with the ability to create initial table entries.


Implementation


Annotations

I have only two abstracts:
  1. Table - applied to a class, describes a table. Properties:
    • Name - the name of the table, if empty - then the name of the class in lowercase is taken.
    • CashedList - indicates whether for instances of this class it is necessary to load each again, or search for it in the cached list (I will explain a bit later).
    • LeftJoinTo - indicates the class to which the given is the heir.
    • RightJoinTo - indicates 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 name of the field in lower case is taken.
    • PrimaryKey - indicates that this field is key.
    • Inherited - Indicates that this annotation is inherited.
    • ReferenceActions - A list of actions for the References field.


Entities

Again we will consider them from an 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 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 Helper's work similar to Helper OrmLite, we get 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);


More on orm

The OrmEntity class has a protected static method:
protected static  List getAllEntities (Class entityClass)


For example, in the CarType class we implement this method:
public static List getAllEntities() {
	return getAllEntities(CarTpe.class);
}


Also in OrmEntity have protected methods alter and the delete . In the BaseEntity base class , I hide alter , and yank it through insert and update , well, purely for beauty.

Checks and limitations

An entity must have a field marked primaryKey . It must be of type Long (namely L ong , so that initially id would be null and ohm alter could understand what it is, insert or update ).
If a field labeled Column is of type List with a class of entities inherited from OrmEntity , then in this class there must be a field of type first class labeled Column .
If the field marked Column is of type inherited from OrmEntity, then this type must have a field labeled Column with primaryKey .
If a class is marked with Table with leftJoinTo , then the class specified in leftJoinTo must be marked with Table in rightJoinTo of which the first class is added.
All failed checks throw an Exception .

Conclusion


Currently, orm is not finished yet, and I am developing it as I need it. The types int , Long , double , String , Drawable are now supported . On the way Document .
From the action on references, while there is only ON DELETE , it can be used to make both CASCADE and SET NULL, and the rest.
LeftJoinTo - while it’s not fully working, now I’m just completing it.
As soon as I finish everything I’ve conceived and comb the code, I’ll post the library on GitHub .

Read Next