Android Room Persistence Library
Bài đăng này đã không được cập nhật trong 6 năm
Android Room Persistence Library
1. Giới thiệu
Room là persistence library cung cấp một abstraction layer trên SQLite cho phép truy cập dễ dàng hơn và khai thác được hết sức mạnh của SQLite.
Ba thành phần chính trong Room:
-
Database: Chứa database holder và phục vụ như điểm truy cập chính cho các kết nối cơ bản. Class sẽ được annotated bằng
@Database
và thỏa các điều kiện sau:- Abstract class và extends từ
RoomDatabase
. - Include danh sách các entities được annotated bằng
@Entity
. - Chứa abstract method không có các arguments và trả về class được annotated bằng
@Dao
.
Ở runtime, ta có thể có một instance của Database bằng việc gọi
Room.databaseBuilder()
hoặcRoom.inMemoryDatabaseBuilder()
. - Abstract class và extends từ
-
Entity: Đại diện cho một table trong database.
-
DAO: Chứa các method được dùng để truy cập vào database.
Room architecture diagram
2. Dependencies
dependencies {
def room_version = "1.1.1"
implementation "android.arch.persistence.room:runtime:$room_version"
annotationProcessor "android.arch.persistence.room:compiler:$room_version" // use kapt for Kotlin
// optional - RxJava support for Room
implementation "android.arch.persistence.room:rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "android.arch.persistence.room:guava:$room_version"
// Test helpers
testImplementation "android.arch.persistence.room:testing:$room_version"
}
3. Entity
Primary key và column
Mỗi entity phải định nghĩa ít nhất một field như một primary key. Thậm chí khi chỉ có 1 field, ta vẫn phải annotate field đó với @PrimaryKey
annotation. Nếu chúng ta muốn Room assign ID tự động, ta có thể set autoGenerate
property của @PrimaryKey
. Nếu entity có một composite primary key, ta có thể dùng primaryKeys
property của @Entity
.
@Entity(primaryKeys = arrayOf("firstName", "lastName"))
data class User(
@PrimaryKey var id,
var firstName: String?,
var lastName: String?
)
Mặt định thì Room sử dụng tên của class làm tên của table, nếu ta muốn chỉnh sửa điều này ta có thể sử dụng tableName
property của @Entity
annotation.
@Entity(tableName = "users")
data class User (
// ...
)
Tương tự vậy, Room cũng dùng tên field làm tên cho column. Để chỉnh sửa điều này ta sử dụng name
property của @ColumnInfo
annotation.
@Entity(tableName = "users")
data class User (
@PrimaryKey var id: Int,
@ColumnInfo(name = "first_name") var firstName: String?,
@ColumnInfo(name = "last_name") var lastName: String?
)
Ignore fields
Room sẽ tạo một column cho mỗi field được định nghĩa trong entity, để Room phớt lờ đi một field và không coi đó là một column, ta phải annotate @Ignore
cho field đó.
@Entity
data class User(
@PrimaryKey var id: Int,
var firstName: String?,
var lastName: String?,
@Ignore var picture: Bitmap?
)
Trong trường hợp entity kế thừa field từ parent entity, ta có thể dùng ignoredColumns
property của @Entity
như bên dưới:
open class User {
var picture: Bitmap? = null
}
@Entity(ignoredColumns = arrayOf("picture"))
data class RemoteUser(
@PrimaryKey var id: Int,
var hasVpn: Boolean
) : User()
Quan hệ giữa các Object
SQLite là một cơ sở dữ liệu quan hệ, nên ta hoàn toàn có thể chỉ ra mối quan hệ giữa các object với Room.
Ví dụ, ta có một entity Book
có quan hệ với entity là User
, ta có thể dùng @ForeignKey
annotation để thể hiện điều này.
@Entity(foreignKeys = arrayOf(ForeignKey(
entity = User::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("user_id"))
)
)
data class Book(
@PrimaryKey var bookId: Int,
var title: String?,
@ColumnInfo(name = "user_id") var userId: Int
)
Foreign keys rất mạnh, nó cho phép ta thấy rõ những gì sẽ xảy ra khi một entity được tham chiếu updated. Cụ thể hơn, ta có thể delete tất cả Book
của User
nếu user này bị delete, bằng việc include onDelete = CASCADE
trong @ForeignKey
annotation.
Create nested objects
Đôi khi ta muốn thể hiện một entity nhanh chóng hoặc POJO (Plain old Java object). Ta có thể dùng @Embedded
annotation.
Ví dụ, một User
chứa một field kiểu Address
, để lưu field này vào table ta sẽ thêm @Embedded
như bên dưới.
data class Address(
var street: String?,
var state: String?,
var city: String?,
@ColumnInfo(name = "post_code") var postCode: Int
)
@Entity
data class User(
@PrimaryKey var id: Int,
var firstName: String?,
@Embedded var address: Address?
)
Table User
sẽ thể hiện các column như sau: id
, firstName
, street
, state
, city
, và post_code
.
Xem thêm: https://developer.android.com/training/data-storage/room/defining-data
4. DAO
Để truy cập vào dữ liệu của App thông qua Room persistence library, ta sẽ làm việc với data access objects, hoặc DAOs.
Insert
Room tạo ra một implementation để inserts tất cả parameters vào database trong một single transaction.
@Dao
interface MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertUsers(vararg users: User)
@Insert
fun insertBothUsers(user1: User, user2: User)
@Insert
fun insertUsersAndFriends(user: User, friends: List<User>)
}
Nếu @Insert
method chỉ nhận một parameter, nó sẽ trả về một kiểu long
thể hiện rowId
cho item mới inserted. Còn khi parameter là một array hoặc collection nó sẽ trả về long[]
hoặc List<Long>
.
Update
Được dùng để cập nhật thay đổi cho một entity. Nó xác định entity cần sửa đổi bằng primary key của entity đó.
@Dao
interface MyDao {
@Update
fun updateUsers(vararg users: User)
}
Delete
Tương tự với Update
, Delete
cho phép xoá một entity thông qua khoá chính của nó.
@Dao
interface MyDao {
@Delete
fun deleteUsers(vararg users: User)
}
Query
Annotation chính được dùng trong DAO classes. Nó cho phép thực hiện read/write vào database với một custom query. @Query
verified lúc compile time. Nên chúng ta dễ dàng phát hiện vấn đề xảy ra khi compile, thay vì một runtime failure.
- Simple queries
@Dao
interface MyDao {
@Query("SELECT * FROM user")
fun loadAllUsers(): Array<User>
}
- Passing parameters into the query
@Dao
interface MyDao {
@Query("SELECT * FROM user WHERE age > :minAge")
fun loadAllUsersOlderThan(minAge: Int): Array<User>
@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>
@Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search")
fun findUserWithName(search: String): List<User>
}
Xem thêm: https://developer.android.com/training/data-storage/room/accessing-data
5. Database
AppDatabase: Liên kết với DAO thông qua một abstract method, việc implement các method của DAO sẽ được thực hiện bởi Room.
@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Cuối cùng ta có thể tạo một thể hiện của database như bên dưới:
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database-name"
).build()
Tham khảo
All rights reserved