Clean Persistence with Sugar ORM

Sugar ORM is a database persistence library that provides a simple and concise way to integrate your application models into SQLite. In contrast to ActiveAndroid, which is mature, powerful, and flexible, Sugar ORM is:

Less verbose

Quicker to set up

More hands-free

Install

Sugar ORM has no dependencies, so installation is as simple as downloading the .jar file and putting it in your libs folder.
It's also available on Maven central. Installing with gradle is as simple as adding implementation 'com.github.satyan:sugar:1.5' to your build.gradle file.

The current stable release is v1.3, but the beta release v1.4_beta is highly recommended. Once you have the .jar file in your libs folder, finishing the installation requires just setting the android:name attribute of the application tag in your AndroidManifest.xml:

There are four additional parameters you can set. DATABASE is the name of the database file that will be created by Sugar ORM. VERSION is the version of the database schema. This is used for schema migrations, which are described in more detail below. QUERY_LOG can be set to "true" or "false" and determines whether to log debug messages for the queries made to the underlying SQLite database. Finally, DOMAIN_PACKAGE_NAME narrows down the packages that Sugar ORM scans for Classes to persist.

Integrate

v1.2

Persisting your models is dead simple. Just extend SugarRecord<YourModel>, and change your constructors to take in a Context object as its first argument, and call super(context) right after the constructor declaration (examples adapted from Designing Your Entities):

This will prevent password from having a column created for it in the underlying database.

v1.3

While the above integration is simple, clean, and quick, integrating with v1.3 is strictly better. The only difference between the two versions in terms of their integration is that Context is no longer a required argument in v1.3. A no argument constructor is the only remaining requirement. The above Author model would become:

Find with conditions

The first query embeds a "where" clause for convenience. The second query is a generic query that more closely proxies raw SQL. Both take a variable number of String arguments equal to the number of ?'s in the query string.

Bulk delete

The first operation will, unsurprisingly, delete every Book record. The second operation only deletes Author records that have an age of 31. Note that for inserting a value into the where clause, it's necessary to make it a String, even though the underlying column is an int.

Member methods

Save

This produces a record in the Author table with columns full_name = "J.K. Rowling" and age = 48.

Delete

Authorauthor=Author.findById(Author.class,23L);author.delete()

This removes the Author with ID = 23 from the database. A common gotcha here is when there is no such Author with this ID, so a null pointer check here is often advisable.

Query Builder

In addition to the above operations, Sugar ORM also comes with a query building interface. Using it allows you to separate the query step from the execution step. In this way, you can save a query and execute it multiple times, where each execution is done against the current database state.

Conditions

The way queries are built using this system is through chaining. That is, an adding another condition or filter returns the Select object back, meaning one can chain together conditions or filters easily. For instance, to create a query to get all Author's whose age is 20 or below, one can make:

This is by no means an exhaustive tutorial on the possible ways of chaining conditions and statements. A more thorough list of possible options can be found here (pending a proper Java doc, this is the best resource for the interface).

Execution

All queries (instances of the Select object) can be saved to be executed later. There are three ways of executing a query, and all have their own purposes:

// Get number of items that would be returned by a query with .count()longnumberYoungAuthors=youngAuthorQuery.count();// Get all of the items corresponding to the query with .list()List<Author>specificAuthors=specificAuthorQuery.list()// Get just the first item that corresponds to the query with .first()AuthorfirstAuthor=specificAuthorQuery.first();

Migrations

Migrations have light support via raw SQL statements. The meta-data tag used earlier in AndroidManifest.xml:

<meta-dataandroid:name="VERSION"android:value="1"/>

contains the version information. This number is used to apply migrations if it is different from the version of the current database.

To create a migration, make a new folder in assets called sugar_upgrades and populate it with <version>.sql files. These files should contain raw SQL statements separated by semicolons. Let's illustrate how this all works with an example. Using the above Author model, add a new field:

publicclassAuthorextendsSugarRecord<Author>{StringfullName;intage;intincome;// This is a new fieldpublicAuthor(){}publicAuthor(StringfullName,intage,intincome){this.fullName=fullName;this.age=age;this.income=income;}}

In assets/sugar_upgrades/, create two files, 1.sql and 2.sql. Leave 1.sql empty because it represents the initial database state at version 1. In 2.sql, write:

altertableAUTHORaddINCOMEINTEGER;

Finally, change the meta-data tag in AndroidManifest.xml to:

<meta-dataandroid:name="VERSION"android:value="2"/>

When the app runs, it will check the version of the current database, and compare that to the version defined in this tag. If they differ, it will apply all of the migrations necessary to reach version 2, in the natural order. In this case, all that needs to be done is to apply 2.sql, which adds an INCOME column to the AUTHOR table.