When developing a more serious Android application, there is a great need to store info about the app state, user profile, game scores, etc. Android provides several ways to accomplish this based on the app's needs (how much space is needed, whether the data should be private or public):
- Shared Preferences
- Internal Storage
- External Storage
- SQLite Database
Shared Preferences are used to persist a small amount of primitive data (int, float, boolean, string, etc) in the key-value format. They are stored even if the application is killed and despite the name, they are not only used to store the current user preferences. Any piece of info that is considered meaningful and helpful for the app can be stored as long as it is in the above specified format.
Internal and External Storage involves writing data to text files and storing them in the desired location. By default, they are saved in the former type of storage of the device and are private (the user and other apps cannot access them). They are lost when uninstalling the application. The latter type of storage can be a SD card and the data is public (the user can alter it if the USB mass storage is enabled).
As we can see the above methods have lots of disadvantages when dealing with more complex operations on persisted data. In this case, SQLite is used.
We will cover using SQLite Database with OrmLite. SQLite is a relational database management system contained in a C programming library. It is ACID(Atomicity, Consistency, Isolation, Durability - properties which make sure that database transactions are processed safely) and implements most of the SQL functionalities. For small apps, developers can work directly with the included android api for databases but this is quite inefficient for bigger and more complex database structure due to writing manual sql queries which slows down development. This is where OrmLite (Object Relational Mapping) comes in. It provides a lightweight functionality for persisting java objects to sql database whilst simplifying queries and scaling. Some features of OrmLite:
- Mark classes to be persisted by using java annotations
- Includes great DAO (Database Access Object) classes
- Advanced query builders for custom sql queries
- Support for foreign objects
- Auto generates sql command to create and drop tables
More information about OrmLite and comprehensive tutorials and documentation at this link http://ormlite.com/.
Implementing a shopping app using OrmLite
To demonstrate the power of OrmLite, we will model and implement a shopping app. To simplify it, we will consider only the shopping lists with products. Also you should separate the classes that work with the database from the rest of the app to make it easier to manage. For example create a new folder in your module called db with two folders in it: model and data.
First of all, we will need to build our models (which will determine the logical structure of the database and in which manner the data can be stored, organized and manipulated). For that, we must determine the way in which our shopping lists will "interact" with products (i.e. determining the relationship between these two models). As a rule of thumb, one should follow this pattern: A shopping list can have multiple products. A product can belong to multiple shopping lists. This is called a many to many relationship and the only way to implement it is to create a third model (table) in which we will store a shopping list and a product. (For more detail on database relationships this tutorial will prove very helpful: sql tutorial). As a result, all the information regarding a shopping list will be found in this third table by querying the database for a shopping list id. But if we stay and think a little about this method, we will see that all the data is scattered and spread across multiple table rows which defeats the purpose of OrmLite (that is to persist java objects).
Solving the many-to-many relationship is slightly different using OrmLite but more intuitively. The third model, let's call it ProductInList, is still necessary because we need something different to store the state of a product in a shopping list (checked, unchecked, quantity, etc). We will use the following structure available at https://assist-software.net/snippets/sqlite-database-ormlite.
The code is quite self explanatory and easy to understand. The novelty is the use of ForeignCollectionField. Instead of keeping all the data related to a shopping list spread across multiple models, we will keep it compact in the ShoppingList model. To also store the products that belong to a shopping list, OrmLite strongly recommends the use of ForeignCollectionField, basically a special type of array in which every member is of ProductInList type. This special array is handled in the background by OrmLite which automatically creates the foreign keys, intermediate helper tables, etc. It automates the hole process making it a lot easier to manage this kind of relationship. A side note: we still need to create the foreign keys in the ProductInList model in order for OrmLite to create a link between the models. (See in the above link about primary keys, foreign keys and designing relationships).
Another thing that worth mentioning is the power of Java annotations and the way they are handled by OrmLite. A change just in a single line of code will propagate throughout the hole database. If we later decide that we don't need any more the checked field, we can just simply remove the @DatabaseField annotation above the declaration of the field without breaking the code(setters, getters, functions that reference to this field, etc).
We haven't finished yet. The three files above should be put in the model folder inside db. Now we must create other classes which will handle all interactions with the database and models. These should be put inside the data folder. The code is available at https://assist-software.net/snippets/sqlite-database-ormlite
DatabaseManager.java is a standard file and DatabaseManager.java contains getters for our DAO objects (through which all interactions to actual tables and database is made) and of course methods OnCreate, OnUpgrade that are self explanatory. We can see that OrmLite has built in queries like createTableIfNotExists, dropTable which are very helpful and automates the process of creating, updating tables and upgrading the db version. (Without this one had to write manual sql queries which are very ugly in java code due to high rate of mistakes and low scaling. Slightly modifying a model enforces rewriting the sql queries ).
Next, we propose implementing some kind of repositories that will implement common database queries over the models, like get all entities, update/create/delete entity, find by id, etc. To accomplish this we need to create another three classes that should also reside in the data folder: ShoppingListRepository, ProductInListRepository, ProductRepository. Only the implementation of the ShoppingListRepository will be shown (we don't want to bore the reader since the other two are similar and can be implemented as a homework) at https://assist-software.net/snippets/sqlite-database-ormlite.
All the functions above implement simple database queries that are handled automatically by OrmLite through the DAO object. Inserting a record has never become so easy. No more hand-written sql queries, subjected to a lot of common mistakes and lack of scalability.
To make use of all this novel functionality in our shopping app main activity we will globally declare the repositories and an array to hold our shopping lists:https://assist-software.net/snippets/sqlite-database-ormlite
The above method of inserting a ProductInList in the ForeignCollectionField is faster and is automatically handled by OrmLite. We could also do this directly by adding it through the theList. getProducts() returned object. It is up to the programmer but it should be done with care (more info here and here ). For the consistency and the scope of this tutorial we will leave as a homework to the reader implementing the actual views, methods, etc . The code above should be regarded as a kind of helper "pseudo-code" and should not be taken as is. There are plenty of things to be done that are not covered in this tutorial.
In conclusion, OrmLite is a powerful ORM for Android that allow developers to store data on Android devices and write high quality code very fast. Also the "Lite" part is taken very serious. As a consequence it has also several disadvantages, but they seem nothing compared to the huge list of advantages that it provides.