Data Access Objects - DAO in Room
Note: This article is part of the advanced Room series which covers all the details about the Room persistence library. You can read all the articles here:
- Introduction to Room Persistent Library in Android
- Data Access Objects - DAO in Room [You are here]
- Entity Relationship in Room
- How does Room work internally?
- Room Database Migrations
- Using Room with LiveData and other third-party libraries
So, let's get started.
In previous articles, we have covered how we can use Room persistence library to create a relational database very easily. Some of the advantages of using Room are compile-time query verification, no boilerplate code and easy integration with RxJava, LiveData and Kotlin Coroutines. All these advantages in Room are achieved using Data Access Objects or DAOs.
In this article, we going to discuss
or
Data Access Objects
in detail.
DAOs
In Room,
or
Data Access Objects
are used to access your application’s persisted data. They are a better and modular way to access your database as compared to query builders or direct queries.
DAOs
A DAO can be either an interface or an abstract class. If it’s an abstract class, it can optionally have a constructor that takes a
as its only parameter. Room creates each DAO implementation at compile time.
RoomDatabase
You can perform multiple operations using DAO like Insertion, Updation, Deletion and making raw queries. Also, you can easily integrate LiveData, RxJava Observables, Kotlin Coroutines in DAOs.
Insertion
When you create a DAO method and annotate it with
, Room generates an implementation that inserts all parameters into the database in a
single transaction.
@Insert
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertUsers(vararg users: User) @Insert
fun insertBothUsers(user1: User, user2: User) @Insert
fun insertUsersAndFriends(user: User, friends: List<User>)
}
annotation parameter signifies what to do if a conflict happens on insertion. It can take the following values:
onConflict
- OnConflictStrategy.REPLACE : To replace the old data and continue the transaction.
- OnConflictStrategy.ROLLBACK : To rollback the transaction.
- OnConflictStrategy.ABORT : To abort the transaction. The transaction is rolled back.
- OnConflictStrategy.FAIL : To fail the transaction. The transaction is rolled back.
- OnConflictStrategy.NONE : To ignore the conflict.
Note: ROLLBACK and FAIL strategies are deprecated. Use ABORT instead.
Updation
When you create a DAO method and annotate it with
, Room generates an implementation that modifies a set of entities, given as parameters, in the database. It uses a query that matches against the primary key of each entity.
@
Update
@Dao
interface UserDao {
@Update(onConflict = OnConflictStrategy.REPLACE)
fun updateUsers(vararg users: User) @Update
fun update(user: User)
}
Deletion
When you create a DAO method and annotate it with
@
, Room generates an implementation that removes a set of entities, given as parameters, from the database. It uses the primary keys to find the entities to delete.
Delete
@Dao
interface UserDao {
@Delete
fun deleteUsers(vararg users: User)
}
Simple queries
is the main annotation used in DAO classes. It allows you to perform read/write operations on a database. Each
@Query
method is verified at compile time, so if there is a problem with the query, a compilation error occurs instead of a runtime failure.
@Query
Room also verifies the return value of the query such that if the name of the field in the returned object doesn’t match the corresponding column names in the query response, Room alerts you in one of the following two ways:
- It gives a warning if only some field names match.
- It gives an error if no field names match.
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun loadAllUsers(): Array<User>
}
Passing parameters into the query
Parameters passed to the DAO methods can be used into the query written in @Query annotation.
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE age BETWEEN :minAge AND :maxAge")
fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>
@Query("SELECT * FROM users WHERE first_name LIKE :search " +
"OR last_name LIKE :search")
fun findUserWithName(search: String): List<User>
}
Returning subsets of columns
You can also return subsets of columns from a query in Room.
data class NameTuple(
@ColumnInfo(name = "first_name") val firstName: String?,
@ColumnInfo(name = "last_name") val lastName: String?
)@Dao
interface UserDao {
@Query("SELECT first_name, last_name FROM users")
fun loadFullName(): List<NameTuple>
}
Direct cursor access
If your app’s logic requires direct access to the return rows, you can return a
object from your queries.
Cursor
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun loadAllUsers(): Cursor
}
Querying multiple tables
Some of your queries might require access to multiple tables to calculate the result. Room allows you to write any query, so you can also join tables. Furthermore, if the response is an observable data type, such as
or
Flowable
, Room watches all tables referenced in the query for invalidation.
LiveData
@Dao
interface BookDao {
@Query(
"SELECT * FROM book " +
"INNER JOIN loan ON loan.book_id = book.id " +
"INNER JOIN user ON user.id = loan.user_id " +
"WHERE users.name LIKE :userName"
)
fun findBooksBorrowedByNameSync(userName: String): List<Book>
}
Query return types
Room supports a variety of return types for query methods, including specialised return types for interoperability with specific frameworks or APIs.
You can return
,
LiveData
and
Observable
from query methods. Also, you can make a DAO method
suspend function
. These are discussed in separate articles.
Flow
This is all about DAO in Room. Hope you enjoyed this blog. In the next blog, we will learn about Entity Relationship in Room .
You can also connect with me on LinkedIn , Twitter , Facebook and Github .
Thank You!!!