Introduction
After a break in the development of my
Android application , during which more and more new ideas were formed in my head, how to make it more beautiful and comfortable, at the end of January I sat down again for development. During my thinking, the approach to creating an application was slightly transformed, and therefore I only got to the object model three weeks ago. And almost immediately faced with the need for refinement
UCAOrm . Who is interested in learning not only about the innovations already implemented, but also about the fact that only during the development process -
Changes and additions
The first thing I encountered was the need for
ContentProvider and
Cursor .
There are
no particular problems with
ContentProvider - the abstract
OrmContentProvider is inherited from
ContentProvider and it still implements two methods:
query , which accepts
OrmWhere and returns
OrmCursor , and
update , which accepts the instance being updated.
OrmCursor is inherited from
AbstractCursor and, besides the implementation of all the necessary methods, it also implements the
getEntities method,
which returns a
List of objects. The most interesting, from the point of view of implementation, are the
getColumnNames function, which returns an array of column names (
has already remade the
getOrmFields function), and the private function
getObject , which returns the value of the specified column. These classes have greatly simplified the development of account synchronization.
The second innovation was the support of new field types:
boolean and
int array . If everything is more or less clear with
boolean , I’ll tell you about the
array in more detail. First, the idea was to create an additional table with the name “
class name_ field name ” and one single column of the type of the array component. However, after speculating, I came to the conclusion that an array with a class inherited from
OrmEntity destroys the entire architecture, and any other non-primitive type the developer still has to be serialized manually. From this, I decided that
orm will only support arrays of primitive types that are perfectly serialized into a string and also perfectly deserialized back. Problems, perhaps, can arise with
double , the format of which in the form of a string can contain a comma, which is the separator of the elements of the array, but they are easily solved by hard setting the locale in
English .
Also, I finally got to the implementation of the
getDefaultValues method in
OrmHelper 's successor. Now it looks like this:
@Override public void getDefaultValues(Class<? extends OrmEntity> entityClass, List<OrmEntity> valueList) { }
accordingly, adding default values ​​for our favorite model from the
second part will be implemented like this:
public void getDefaultValues(Class<? extends OrmEntity> entityClass, List<OrmEntity> valueList) { if (entityClass.equals(CarType.class)) { valueList.add(new CarType("Passenger")); valueList.add(new CarType("Truck")); } }
Well, now we have
gotten close to the most delicious problem that
hardex spoke
about in the
first article - updating the data schema.
Updating data schema
Again, back to our model and consider the essence of
Car :
@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; }
Suppose we decide to add another field:
@Column(name = "max_speed") private int maxSpeed;
In this case, we need to change the version of the database in the manifest:
<meta-data android:name="UO_DB_VERSION" android:value="2" />
And write the code in the
onUpdate helper method:
@Override protected void onUpgrade(int oldVersion, int newVersion) { if (newVersion == 2) { OrmUtils.UpdateTable(Car.class).work(); } }
“
Why else do we need a work method? ”- someone will ask. And let's consider possible options for changing the data schema:
- A new field is added to the schema.
- The field is removed from the schema.
- The field is renamed.
- The field changes type.
Most likely, many have already guessed that the only item that does not cause difficulties is the first one, but we will consider them in order.
')
Field additions
Everything is easy here:
orm scoops the fields from the table and compares it with the fields from the class. When a new field is found in the object model, it jerks for it
ALTER TABLE … ADD COLUMN …
If you need a default value, you will need to specify it in the annotation.
Deleting a field
The beginning of the algorithm is similar to the previous one: we compare the fields and find those that need to be deleted. Well, then, almost, as indicated in
faq'e . The only thing I don’t understand is why the second copy is needed, because after the
drop 'zeros of the table, you can just rename the temporary one and it will become permanent!
Rename field
And here
work is not your assistant!
Orm simply will not understand that you just renamed the field and will do two things: remove the field with the old name from the base and add a new one with the new one. Of course, it was also possible to beat this in the annotation by adding the
old_name field, but it seemed to me that this was too much, and the
orm could be unloaded,
telling him exactly what to do. In light of the foregoing in this paragraph, we need the
rename method:
OrmUtils.UpdateTable(Car.class).renameColumn("old_column_name", "new_column_name");
Note that you need to specify the name of the column, not the field! As a result,
orm will not wool the entire class and all fields in the database in order to understand what it needs to change, but simply make changes to the name of one single column.
Also, we can help him add a column:
OrmUtils.UpdateTable(Car.class).addColumn("column_name");
and delete the column:
OrmUtils.UpdateTable(Car.class).deleteColumn(“column_name”);
The very same renaming in the form of a
sql query caused some questions. At first, I decided this, as well as the deletion, by creating a new table with the desired field name, where the data is copied from the old one, and it is simply deleted, and the new one is renamed. But then, I stumbled upon
this article and plan to try this method.
Change field type
Orm, again, can do everything for you, but you can help him:
OrmUtils.UpdateTable(Car.class).changeColumnType("column_name");
In principle, the second parameter could also be passed on a new column type, but I did not want to give the programmer the opportunity to specify the wrong type, and then scold
orm (:-)). The problem of incompatibility of data of the old type and the new one is solved by the base itself, throwing an exception when copying the old table into a new one. But the column can be simply zeroed by passing
true as the second parameter:
OrmUtils.UpdateTable(Car.class).changeColumnType(“column_name”, true);
There was also a parameter in the design that indicates that the field should be reset only in case of incompatibility of types, but so far did not become.
Conclusion
UCAOrm has undergone such changes over the past two weeks. Not everything has been laid out in
github , since, as I wrote a little higher, the work on
Updater is still underway, and not everything has been tested yet. There is also an idea to slightly simplify the initial creation of the tables: simply by calling
OrmUtils’s createByPackeg method, passing the package name there, in which
orm will look for marked classes. But this is only an idea.
As always I will be glad to any new ideas and suggestions. Wait for updates soon.