Skip to main content

Basics of Room Persistence Library in Android #RP1



Room is a library built on top of SQLite that provides a more convenient and structured way to manage your app's local database. It lets you work with your data as Kotlin objects (data classes) instead of manually formulating them using SQL queries, which itself is a tedious task prone to many errors. 

It also simplifies interactions with the database with many great features like validating the queries, seamless integration with Kotlin Flow, paging, LiveData, and many more. These features make Room the most preferred option for offline-first apps, as it expedites the process and helps us to avoid issues early in the development cycle.

Since Google has the best article for setting up, I will be skipping the setup

Dependency setup: Official Guide on Setting up Room Dependencies and plugins 

Please do ensure you follow the above guide and then sync and build your project successfully, as it will help you to try out the components yourself. In this article, my intention is to explore the key components of Room, a bare minimum you need to know to implement a simple offline-first app. For a detailed and more complex use case, check out my upcoming article.

Key Components

Primarily, there are three important components: Entity, DAO's, and Database, The entity represent a table. Dao's are an interface to write queries and define your operations. The database is where you associate your entities and include the dao's you would like to access from outside; hence, it's the entry point of your database. Let's check it out one by one:

Entity

Any class annotated with @Entity is an entity, and those classes represent a table in the database. Here is an example of what a simple entity would look like:

An entity must have a minimum of one parameter that is annotated with @PrimaryKey(), and it would be the primary key for uniquely identifying a single row; the value of the parameter must be unique; otherwise the app will crash.

You can set the autogenerate to true just like the above code snippet. Ensure the type is primitive; I prefer Long, but if you are expecting very few entries, you can use Short; otherwise, you decide it according to your needs. If you have no clue, then stick with Long.

The entity can be further modified with a custom name for the table, indexing, custom column names, etc. We can discuss those in the upcoming articles. 

DAOs

Short for Data Access Object, as the name suggests, this is the interface between the developer and Room for accessing the tables in a safe and simplified way. It's basically an interface with an annotation with @Dao; inside you can use Room query annotations to access and modify the tables as per your requirements. Here is a sample code snippet of how a DAO for our case will look like:

As you can see in the above snippet, Room simplifies the process of implementing an offline-first app. Though it looks simple, don't assume that Room is only for simple use cases; I have kept it minimal to not overcomplicate it. We will definitely go through the complex use cases in the upcoming articles.

The interface annotated with @Dao is typically called Dao's; here our Dao is called HabitTrackerDao.''It is preferred to name your Dao with a postfix 'Dao.'.

I would highly recommend you access only one entity (table) in a single DAO, but Room does not restrict you from accessing multiple entities (tables) in the same DAO, but trust me, this will be helpful as your product grows. We will talk about how to handle the complex cases in the next article on Room.

 Do note There is no restriction on the number of Dao's you can have; just ensure the names don't conflict with each other, and the minimum target can handle it (no need to worry if your entities are less than 50).

Database 

Now we have tables or entities and the DAOs that enable us to access and interact with the tables. Now, there can be multiple databases in an Android app, so how does Room know that the entity and DAO are part of which database? That's exactly what we are going to define next.

Take a look at the following code snippet of our simple database:

This might be a little tricky, but it's pretty simple. We are creating an abstract class that extends the RoomDatabase and also annotating it with @Database to represent the class as a database and letting the room compiler know that this is one of the databases and the declared entities inside the array are to be associated with this database, and this serves as an entry point to the database.

The entities array as of now has only one entity for simplicity, so don't forget to add all the entities in this array. Another thing to note is that whenever you are modifying the entities, ensure you upgrade the version of the database to avoid a crash. The reason for this crash is that Room does not know how to handle the schema changes unless a migration strategy is provided; we will talk about this in the next article. In a real-world application, the table could contain user data, and we usually want to preserve this data during schema updates, making migrations a crucial part of database version management.

Manually Creating a Database and Interacting

Hooray! Finally, we are here. You can build it for accessing the database and interacting with it easily. Do check out the code below; it shows a simple manual creation of the database. 

We are using the DatabaseBuilder function from the Room Companion object to build our database; the result of the build is our own implementation of the requested database, with which we can get the implementation of our DAO's from Room and do our interactions... 

Do ensure you don't use the UI thread for accessing the database, as it can cause issues; otherwise, you should not have any other issue, but if you face any issue, feel free to start a discussion below:

This is a simple implementation, but a typical app developer would use a DI. Whether to use DI or not is your decision,, depending on the scale of your project. but I will be using it in the final project after this series of articles.

Thanks for reading so far. While this already feels like a lot, these could be enough for your first simple projects, but there are a lot of other interesting cases we have not yet talked about, like @RawQuery, Junctions, TableViews, and many more, which we shall discuss in the next article.

feel free to share your feedacks and suggestions below

Comments

Popular posts from this blog

Designer PDF Viewer - HackerRank Problems

Difficulty: EASY Problem : The objective here is to find the size of the highlighted area, and we are given the size's of all the alphabets, we have to find the largest alphabet in the highlighted word and then calculate the size of the rectangle so if the tallest character is 3 then the size of the box will be 3 * number of characters given. Visual representation of the selection : abc def ghij Inputs An array with the sizes of all alphabets a-z in order. A String of highlighted words. Important points to note The array which holds the height of each character in ascending order which means the arrays 0th index will have the height of a 1st index will have the height of b and so on and so forth in the end the hight of z will be there so it's easy to locate each character. A String with the highlighted word. This means we have got the characters inside the rectangle, all we have to find is ...

Literals of Base numbers in Java ( Octal , Hexadecimal, Decimal)

1. Overview: A literal key indicates the compiler how to interpret the value of the given data type, for numbers we can calculate the value by using Octal representation or hexadecimal representation but just typing out a hexadecimal value to an int will throw us an error because the compiler has no idea how to handle it but if we assign the java specified prefix for the required bases with some literals then the compiler will not throw us any error as it understands how to interpret the value.  Base Litrals Values Example Eg. Value Decimal none 0-9 int x = 10; x is 10 Octal 0 (zero) as the prefix 0-7 int x = 12; x is 10 Hexadecimal 0x or (zero) along with an x 0-9 and a-f or A-F int x = 0XA; x is 10 Binary or Base(2) Allowed Digits 0 and 1 int i = 10; and now the variable i has value 10. i...

Array List - Collections Framework in Java - DSA

Gist: An array list can store individual objects by following insertion order, here the initial capacity is 10 by default but can be modified as per the requirement, once the array list reaches its load factor then internal all the elements of the current array is copied to a new array with the new capacity and the reference variable will now be referring to this new array list and the old array will be dealt by the garbage collector. Hierarchical order Type of constructors Empty argument constructor or the default constructor is the same as invoking any other object here a new ArrayList is created with a default size of 10. Below is the most commonly used constructor by beginners and others alike. ArrayList array = new ArrayList(); //array has a capacity of 10 The default constructor above will allot only 10 slots but if you want the initial size to be 20 or 1000 you can do so with the following constructor this is ideal w...