Card image cap

Table of Contents:



Introduction


In this blog post I'm going to show you how to get started with SQLite and the Room Persistence Library on Android.

If you want more information, I made a course on implementing the Room Persistence Library. It's free and available here.


What is SQLite?


SQLite is a type of relational database. This blog post is focused on the Room Persistence Library so I won't be going into detail about SQLite on Android. But I encourage you to read more about SQLite if you don't know much.

If you know absolutely nothing about SQLite watch this video. It's part of my SQLite for Beginners 2019 course. It'll ask you to register but don't worry it's free and only takes about 30 seconds.

Here's what you need to know:
When an android app is installed on a device, an SQLite database is automatically generated. As the developer, you can access this database and use it to save data. The most common use for this internal SQLite database is for building an offline database cache.

In March 2019, I'll be making a course on how to build a database cache. Make sure to register to be notified when it's published.


What is the Room Persistence Library?


Room is an SQLite object mapping library. It was built to assist developers implementing local SQLite database transactions. It helps to avoid the boilerplate code that was previously associated with interacting with the SQLite database.

Essentially the room persistence library allows developers to easily convert SQLite table data into java objects. Room provides compile time checks of SQLite statements and can return RxJava, Flowable, and LiveData observables. In this blog post I'll be showing you an example using LiveData.


Architecture Overview

sqlite room persistence library architecture


This is the overall architecture of what I'll be implementing.

Activity:
The Activity is obviously just an activity. This is where the code will live for interacting with the database.

Repository:
A Repository is a class that abstracts access to multiple data sources. The Repository is not part of the Architecture Components libraries, but is a suggested best practice for code separation and architecture. A Repository class handles data operations. It provides a clean API to the rest of the app for app data.

Room Database:
This is the "meat and potatos" of the Room Persistence implementation. There will be 3 classes:

  1. An Entity class:
    The Entity class will define the rows of the SQLite database. This will essentially be a model of the data you want to store.

  2. The DAO (Data Access Objects):
    In the DAO (data access object), you specify SQL queries and associate them with method calls. The compiler checks the SQL and generates queries from convenience annotations for common queries, such as @Insert, @Update, @Delete and @Query.

  3. The Database:
    The database is exactly what it sounds like. It's the database. This class will contain the code required to instantiate the SQLite database object. Usually, you only need one instance of the Room database for the whole app. That's why I'll be implementing the singleton pattern.


Dependencies



Make sure you have the most up-to-date version here.


Building the Entity


The Entity class depends on what kind of data you're trying to model. For the purpose of this example, I'm going to pretend as though I was building a note-taking application. Here's what an Entity class might look like for a Note object.


  • @Entity:
    Each @Entity class represents an entity in a table. Annotate your class declaration to indicate that it's an entity. Specify the name of the table if you want it to be different from the name of the class.

  • @PrimaryKey:
    Every entity needs a primary key. To keep things simple, each word acts as its own primary key.

  • @NonNull:
    Denotes that a parameter, field, or method return value can never be null.

  • @ColumnInfo:
    Specify the name of the column in the table. Note that the name attribute isn't required. You only need to use it if you want to change the name of the column. By default it will be whatever the object is named.

  • @Ignore
    Ignores the marked element from Room's processing logic. Since I'm using two constructors here, I need to tell Room which one to use. So I'm ignoring the empty one.

  • Getter and Setters
    Every field that's stored in the database needs to be either public or have a "getter" method.


The DAO (Data Access Objects)


The DAO is an interface. Personally, I think the best way to think of the DAO is by the literal meaning of the word. It's an interface. It's an interface into the database. It contains all the methods responsible for accessing the database.


  • @Insert:
    Room Persistence convenience annotation for database inserts. You can optionally return a long[] value. The long[] denotes how many rows were successfully inserted.

    Optionally you can provide a conflict strategy. For example if you want to replace a row if a conflict is detected, you can specify a "conflict" attribute like this

    @Insert(onConflict = OnConflictStrategy.REPLACE)


  • @Query:
    In this example I'm using the @Query annotation to retrieve all the notes from the database. But you can use @Query for anything. Meaning, you could do inserts, deletes, updates, anything... You just write in the custom SQL you want to execute, and away it goes.

    Notice the use of LiveData here. If you use a return value of type LiveData in your method description, Room generates all necessary code to update the LiveData when the database is updated. Very convenient. I recommend using this whenever possible.

  • @Delete:
    Room Persistence convenience annotation for database deletes. You can optionally return an int value. The int denotes how many rows were affected by the transaction.

  • @Update:
    Room Persistence convenience annotation for database updates. You can optionally return an int value. The int denotes how many rows were affected by the transaction.



The Room Database


Room is a database layer on top of an SQLite database. Room takes care of mundane tasks that you used to handle with an SQLiteOpenHelper.


This is the class responsible for instantiating an instance of your room database. Notice that I'm implementing the singleton pattern. I'm implementing the singleton pattern to prevent having multiple instances of the database opened at the same time.

Notice it's referencing the Note entity with the entities attribute inside the @Database annotation.

Also notice the getNoteDao() method. That's to get a reference to the DAO from the repository. I'll show you the repository next.


AsyncTasks for Background Operations


All database transactions with Room must be done on a background thread.

A simple way to make sure all the operations are done on background threads is to use an AsyncTask. To stay consistent with the theme of segmenting code, I recommend making 3 different AsyncTask classes. One for inserting data, updating data, and deleting data.

Notice that I didn't mention retrieving data. Read #4 below to find out why.

  1. Inserting Data:


  2. Updating Data:


  3. Deleting Data:


  4. Retrieving Data:
    I don't need to use an AsyncTask when retrieving data from Room. Can you guess why? It's because I used LiveData. Right now this isn't going to make sense since I haven't shown the Repository yet. You're not going to know where the retrieveNotesTask() method comes from. It'll make sense once you read the section below.



The Repository


As I stated in the introduction, a Repository is a class that abstracts access to multiple data sources. The Repository is not part of the Architecture Components libraries, but is a suggested best practice for code separation and architecture. A Repository class handles data operations. It provides a clean API to the rest of the app for app data. Like a bridge between the views and the database.

In this example I only have a single data source. But I still think it's good practice to use a repository anyway.


Inserting, updating and deleting data are all done using the AsyncTasks that I showed you in the previous section. Retrieving data is a little different.

Retrieving data is done using the LiveData class. By default, a method that returns LiveData will execute asychronously. That's why I don't need to use an AsyncTask.

If you want to execute the query, just instantiate a NoteRepository object and attach an Observer. Here's an example of what that might look like in an activity:

Retrieving Notes:



Inserting Notes:



Updating Notes:



Deleting Notes:




Final Thoughts


The Room Persistence Library is a huge step up from the old way of accessing the internal SQLite database. It's much more organized, easier to read, and has a ton of convenience methods to prevent developers from making silly mistakes.

Need some extra help?.
I made a course on implementing the Room Persistence Library. It's free and available here.


Staying in the Loop


If you want to stay in the loop and get an email when I write new blog posts, Follow me on Instagram or join the CodingWithMitch community on my website. It only takes about 30 seconds to register.





Authors


Mitch Tabian

codingwithmitch.com

Software Dev


Create an Account



Have an account? Log In

CodingWithMitch Members

Unlimited access to all courses and videos

Step by step guides to build real projects

Video downloads for offline viewing

Members can vote on what kind of content they want to see

Access to a private chat with other communnity members & Mitch

Become a Member

CodingWithMitch Members

Unlimited access to all courses and videos

Step by step guides to build real projects

Video downloads for offline viewing

Members can vote on what kind of content they want to see

Access to a private chat with other communnity members & Mitch

Become a Member

Comments