Room Persistence Library. Part 1.

Beauty Coder
Beauty Coder
Published in
4 min readJan 23, 2018

The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

Since my last article Room database library from alpha version become a 1.0.0. So now you can use it without any worries in your project.

Adding to a project

dependencies {
// Room
implementation "android.arch.persistence.room:runtime:1.0.0"

annotationProcessor
"android.arch.persistence.room:compiler:1.0.0"

// Test helpers for Room
testImplementation "android.arch.persistence.room:testing:1.0.0"
}

First, you need to define your Entity objects.

Entity represents a table within the database.

@Entity
class GeoLocation {

@PrimaryKey(autoGenerate = true)
var id: Int? = null
var latitude: Double? = null
var longitude: Double? = null
var timestamp: Long? = null



constructor()

constructor(latitude: Double?, longitude: Double?, timestamp: Long?) {
this.latitude = latitude
this.longitude = longitude
this.timestamp = timestamp
}
}

Some of the most useful annotations:

@PrimaryKey
Use the @PrimaryKey annotation to set which property is going to be a primary key.

@ColumnInfo
You can also define a custom column name with the @ColumnInfo(name = "your_name")

@Ignore
Use the @Ignore annotation to ignore some properties. The property with @Ignore annotation won't be stored in database.

Next step you need to create your DAO objects.

To access your app’s data using the Room persistence library, you work with data access objects, or DAOs. This set of Dao objects forms the main component of Room, as each DAO includes methods that offer abstract access to your app’s database.

@Query — an annotation to define a query for the database.

@Insert — an annotation is using for inserting objects to database. You can define onConflict value for this annotation. This one I really needed.

@Update — use the update annotation to update rows in the database if they already exist (checked by primary keys). If they don’t already exist, this option will not change the database.

@Delete — an annotation to delete objects from the database.

@Dao
interface GeoLocationDao {

/**
* Get locations LiveData.
* Can be called from the main thread.
* @return LiveData of GroLocation list.
*/
@Query("SELECT * FROM geoLocation ORDER BY timestamp DESC")
fun getLocationsLiveData(): LiveData<List<GeoLocation>>

/**
* Get locations list.
* Can be called from the main thread.
* @return List of GroLocation objects.
*/
@Query("SELECT * FROM geoLocation ORDER BY timestamp DESC")
fun getLocations(): List<GeoLocation>

/**
* Insert new locations.
* Have to ba called in background thread.
* @param location GeoLocation object.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg location: GeoLocation)

}

Next step you need to make is create abstract database class with @Database annotation. There you have to set list of entities and database version. In your class, you need to define methods to get access to your DAO objects. In my case, it’s only one method to get LocationDao.

@Database(entities = arrayOf(GeoLocation::class), version = 1)
abstract class AAEDatabase : RoomDatabase() {
abstract fun geoLocationDao(): GeoLocationDao
}

I use Application class to create an instance of Database class. You can do any other way.

class AApplication : Application() {

override fun onCreate() {
super.onCreate()
appContext = applicationContext
database = Room.databaseBuilder(this, AAEDatabase::class.java, "aae-db").build()
}


companion object {
var appContext: Context? = null
var database: AAEDatabase? = null
}

}

For complex data, you can use @TypeConverter. For example some of your entity have as property some custom object or list, map or anything else. If this custom object only being used inside this exact object you probably don’t want to create an additional table for the data.

For example, this class converts Person object to JSON string and back with Gson library.

public class Converter {

@TypeConverter
public String stringFromPerson(Person value) {
if (value == null) {
return null;
}
String json = new Gson().toJson(value);
return json;
}

@TypeConverter
public Person personFromString(String value) {
if (value == null) {
return new Person();
}
Type type = new TypeToken<Person>() {}.getType();
Person person = new Gson().fromJson(value, type);
return person;
}

}

And in your entity object, you need to annotate the property you want to convert with @TypeConverters annotation.

@TypeConverters({Converter.class})
private Person person;

Now when our database is ready, we need to access it.

All requests to the database by default have to be made in background thread. Otherwise, your application will crash. To allow make queries in the Main thread, you can use allowMainThreadQueries() method.

database = Room.databaseBuilder(this, AAEDatabase::class.java, "aae-db").allowMainThreadQueries().build()

Room database can return object or list of objects.

@Query("SELECT * FROM geoLocation ORDER BY timestamp DESC")
fun getLocations(): LisGeoLocation

Or

@Query("SELECT * FROM geoLocation ORDER BY timestamp DESC")
fun getLocations(): GeoLocation[]

Also, you can use new Android Architecture component LiveData like I used in the example. LiveData is an observable object. As soon as data is ready you’ll get an update on your LiveData.

@Query("SELECT * FROM geoLocation ORDER BY timestamp DESC")
fun getLocations(): LiveData<List<GeoLocation>>

If you are using RxJava you can also make you Query return Flowable object.

For that, you need to add new dependency in Gradle.

implementation "android.arch.persistence.room:rxjava2:1.0.0"

And then

@Query("SELECT * FROM geoLocation ORDER BY timestamp DESC")
fun getLocations(): Flowable<List<GeoLocation>>

You can find some working examples of LiveData and Room Persistence Library on my GitHub.

--

--