SQLite & Android Logos

ActiveAndroid Basics – Models

Scott Carson | 09/23/2016

All code for this series is located here.

In the previous post I went over how to get ActiveAndroid up and running in your application, now we are going to implement our first Models. Disclaimer: This post will be longer than the previous two and will include some basic SQLite knowledge bombs.

First, I want to share my idea for the simple app that we will be building upon through this series. We’ll start with the classic Car example. So what do you need to build a car? I’m going to include wheels, an engine, a body, and a serial number in my Car class. The informal diagram below summarizes the Models that we will be creating.

Database Design Screenshot 1 Informal class diagram describing our application models

The eventual idea here is to be able to create and save custom cars to a database and be able to edit them later, much like a car manufacturer would do when servicing custom car orders. Now that we have a basic idea of what we are aiming for, let’s get started with some code.

Below is the first iteration of our Car class which I will break down piece by piece:

Car.java

`package com.rscottcarson.activeandroid_tutorial.models;

import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.google.gson.JsonArray; import com.google.gson.JsonObject;

import java.util.List;

@Table(name = Car.TABLE_NAME) public class Car extends Model {

// Declare table name as public
public static final String TABLE_NAME = "Cars";

// Declare all column names private
private static final String COLUMN_ENGINE = "engine";
private static final String COLUMN_BODY = "body";
private static final String COLUMN_WHEEL = "wheels";
private static final String COLUMN_SERIAL_NUMBER = "serialNumber";

@Column(name = COLUMN_ENGINE)
private Engine engine;

@Column(name = COLUMN_BODY)
private Body body;

@Column(name = COLUMN_WHEEL)
private List<Wheel> wheels;

@Column(name = COLUMN_SERIAL_NUMBER,
        unique = true,
        onUniqueConflict = Column.ConflictAction.REPLACE,
        index = true)
private int serialNumber;

/**
 * Mandatory no arg constructor
  */
public Car(){
    super();
}

// Getters, setters, and support methods removed for clarity

...

}`

Let’s first take a look at how we create a Table using ActiveAndroid.

`@Table(name = Car.TABLE_NAME) public class Car extends Model {

// Declare table name as public
public static final String TABLE_NAME = "Cars";`

The @Table annotation tells ActiveAndroid that we are going to create a SQL table named Cars. It is really that simple. Note that our Car class extends com.activeandroid.Model. This is required for ActiveAndroid to find our classes, create our database, and save to the database. The @Table annotation can also take another argument, a string Id, but we will stick with the default for the time-being. Now let’s look at how we create columns in our table:

`// Declare all column names private private static final String COLUMN_ENGINE = “engine”; private static final String COLUMN_BODY = “body”; private static final String COLUMN_WHEEL = “wheels”; private static final String COLUMN_SERIAL_NUMBER = “serialNumber”;

@Column(name = COLUMN_ENGINE)
private Engine engine;

@Column(name = COLUMN_BODY)
private Body body;

@Column(name = COLUMN_WHEEL)
private List<Wheel> wheels;

@Column(name = COLUMN_SERIAL_NUMBER,
        unique = true,
        onUniqueConflict = Column.ConflictAction.REPLACE,
        index = true)
private int serialNumber;`

In SQLite tables, columns represent individual fields or data types. Each row can be thought of as an entry into the database (our objects themselves). I first declare all of the column names that we are going to use as private constants. This is not required, just something I do to keep my code clean and organized. It also makes it simple to change column names or use them later. Next you’ll see there are four @Column annotations. Each one provides a name for the column, as well as the data that column will represent. The picture below represents what the SQLite table would look like after four cars were added.

Cars Database Table Visual representation of the Car table after 4 cars are added

The first two member variables, Engine and Body are simply other objects in our application domain. As mentioned in my first post, SQLite stores data as one of four types: INTEGER, REAL, TEXT, or NONE. Primitive data types are mapped to one of these SQLite types – int and boolean map to INTEGER, float and double map to REAL, etc. Strings obviously map to the TEXT type, so what happens to these custom objects that we have created? How does ActiveAndroid (and SQLite) reduce these objects into a datatype that it can store?

In databases there is a concept of keys. Since the objects referenced in our Car class are also derivatives of the Model class, ActiveAndroid generates a foreign key that, in essence, maps to the other objects’ tables. Specifically, the foreign key maps to a specific column’s primary key. This concept underlies much of Object-Relational Mapping and allows us to save complex object hierarchies to a SQL database. The answer to the original question is that foreign keys are saved as the TEXT SQLite data type which is important to remember further down the line when we look at schema migrations.

The third column and member variable is an interesting one – it is a List of Wheel objects. It is interesting because ActiveAndroid can not handle saving Java’s List types. There are two solutions to this problem. One would be to write a custom List class that implements Java’s list interface and extends Model. The better solution is to write a custom TypeSerializer that tells ActiveAndroid how to map a given type to a SQLite type for saving, and vice-versa for queries. We will do later in the series, for now just know that our List<Wheel> will not be saved properly.

Finally, the fourth column contains some additional arguments that are very useful for customizing your database. After declaring the column name we set unique = true (default is false); this tells ActiveAndroid to set up our table so that each serialNumber is a guaranteed unique value. Next we set onUniqueConflict = Column.ConflictAction.REPLACE; this lets ActiveAndroid know that if we save a car with a serial number that already exists we want to overwrite the existing one. Other possible actions include: FAIL (default), ABORT, IGNORE, and ROLLBACK – I’ll point you to the SQLite documentation to see the differences between these polices. The last customization we make is to set index = true; doing so adds an index to this column in the underlying SQLite table that can speed up queries. In all honesty our app will not be large enough to warrant column indexing, but it is a useful feature to know about. The image below shows all of the possible @Column annotation arguments which I will cover in more depth later in the series when we talk about advanced ActiveAndroid features.

SQLite Documentation Screenshot Possible @Column annotation arguments and their default values

The final piece of our Car class to look at is the mandatory no-arg constructor.

/** * Mandatory no arg constructor */ public Car(){ super(); }

ActiveAndroid utilizes the no arg constructor when servicing database queries, so you must include this in all of your model classes. Do not forget to make the call to construct the superclass!

Other than the getters and setters, a few helper methods were also removed that I will go over in the next post. They are not related to ActiveAndroid, but are there to help display our data. Below I have included the other Model classes that are part of our application, again without the getters, setters, and helper methods.

CarPart.java

`package com.rscottcarson.activeandroid_tutorial.models;

import com.activeandroid.Model; import com.activeandroid.annotation.Table; import com.google.gson.JsonObject;

@Table(name = CarPart.TABLE_NAME) public class CarPart extends Model {

public static final String TABLE_NAME = "CarPart";

public CarPart(){
    super();
}

public JsonObject getJsonObject(){
    return null;
} }`  

The CarPart class is what all of our other parts (Body, Engine, and Wheel) will be derived from. This is mostly to show that as long as the superclass inherits ActiveAndroid’s Model class, all subclasses are also Models.

Body.java

`package com.rscottcarson.activeandroid_tutorial.models;

import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.google.gson.JsonObject;

@Table(name = Body.TABLE_NAME) public class Body extends CarPart {

public static final int BODY_STYLE_SPORT = 1;

public static final String TABLE_NAME = "Bodies";

private static final String COLUMN_BODYSTYLE = "bodyStyle";

@Column(name = COLUMN_BODYSTYLE)
private int bodyStyle;

// Mandatory no-arg constructor
public Body(){
    super();
}

// Getters, setters, and helpers removed for clarity

...

}`

Engine.java

`package com.rscottcarson.activeandroid_tutorial.models;

import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.google.gson.JsonObject;

@Table(name = Engine.TABLE_NAME) public class Engine extends CarPart {

public static final String FUEL_TYPE_GAS = "gas";
public static final int CYLINDERS_FOUR = 4;
public static final double VOLUME_3_7_L = 3.7;

public static final String TABLE_NAME = "Engines";

private static final String COLUMN_VOLUME = "volume";
private static final String COLUMN_CYLINDERS = "cylinders";
private static final String COLUMN_FUELTYPE = "fuelType";

@Column(name = COLUMN_VOLUME)
private double volume;

@Column(name = COLUMN_CYLINDERS)
private int cylinders;

@Column(name = COLUMN_FUELTYPE)
private String fuelType;

// Mandatory no-arg constructor
public Engine(){
    super();
}

// Getters, setters, and helpers removed for clarity

...

}`

Wheel.java

`package com.rscottcarson.activeandroid_tutorial.models;

import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.google.gson.JsonObject;

@Table(name = Wheel.TABLE_NAME) public class Wheel extends CarPart {

public static final String RIMS_SPINNER = "spinner";
public static final String TIRES_WRANGLER = "Goodyear wrangler";

public static final String TABLE_NAME = "Wheels";

private static final String COLUMN_RIMS = "rims";
private static final String COLUMN_TIRES = "tires";

@Column(name = COLUMN_RIMS)
private String rims;

@Column(name = COLUMN_TIRES)
private String tires;

// Mandatory no-arg constructor
public Wheel(){
    super();
}

// Getters, setters, and helpers removed for clarity

...

}`

All three of the models shown above use primitive day types that map directly to a SQLite value and are otherwise pretty self-explanatory.

What is next?

In the next post we will build our first iteration of the UI and learn the basics of saving and querying.

Part 4: Let’s Build a Car!

Get a Quote