Lưu dữ liệu trong cơ sở dữ liệu cục bộ bằng Room Một phần của Android Jetpack.
Những ứng dụng xử lý lượng dữ liệu có cấu trúc với lượng không nhỏ có thể được hưởng nhiều lợi ích khi lưu dữ liệu đó trên máy. Trường hợp sử dụng phổ biến nhất là lưu các phần dữ liệu có liên quan vào bộ nhớ đệm. Bằng cách này, khi thiết bị không thể truy cập mạng, người dùng vẫn duyệt qua được nội dung đó.
Thư viện lưu trữ Room cung cấp một lớp trừu tượng qua SQLite để mang lại khả năng truy cập cơ sở dữ liệu dễ dàng, đồng thời khai thác toàn bộ sức mạnh của SQLite. Cụ thể, Room đem lại các lợi ích sau:
- Xác minh thời gian biên dịch của truy vấn SQL.
- Chú thích tiện lợi giúp giảm thiểu mã nguyên mẫu lặp lại, dễ mắc lỗi.
- Hợp lý hoá đường dẫn di chuyển cơ sở dữ liệu.
Do những điểm cần lưu ý này, bạn nên dùng Room thay vì sử dụng trực tiếp API SQLite.
Thiết lập
Để dùng Room trong ứng dụng của bạn, hãy thêm các phần phụ thuộc sau vào tệp build.gradle
của ứng dụng:
Groovy
dependencies { def room_version = "2.6.1" implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version" // To use Kotlin annotation processing tool (kapt) kapt "androidx.room:room-compiler:$room_version" // To use Kotlin Symbol Processing (KSP) ksp "androidx.room:room-compiler:$room_version" // optional - RxJava2 support for Room implementation "androidx.room:room-rxjava2:$room_version" // optional - RxJava3 support for Room implementation "androidx.room:room-rxjava3:$room_version" // optional - Guava support for Room, including Optional and ListenableFuture implementation "androidx.room:room-guava:$room_version" // optional - Test helpers testImplementation "androidx.room:room-testing:$room_version" // optional - Paging 3 Integration implementation "androidx.room:room-paging:$room_version" }
Kotlin
dependencies { val room_version = "2.6.1" implementation("androidx.room:room-runtime:$room_version") annotationProcessor("androidx.room:room-compiler:$room_version") // To use Kotlin annotation processing tool (kapt) kapt("androidx.room:room-compiler:$room_version") // To use Kotlin Symbol Processing (KSP) ksp("androidx.room:room-compiler:$room_version") // optional - Kotlin Extensions and Coroutines support for Room implementation("androidx.room:room-ktx:$room_version") // optional - RxJava2 support for Room implementation("androidx.room:room-rxjava2:$room_version") // optional - RxJava3 support for Room implementation("androidx.room:room-rxjava3:$room_version") // optional - Guava support for Room, including Optional and ListenableFuture implementation("androidx.room:room-guava:$room_version") // optional - Test helpers testImplementation("androidx.room:room-testing:$room_version") // optional - Paging 3 Integration implementation("androidx.room:room-paging:$room_version") }
Thành phần chính
Có 3 thành phần chính trong Room:
- Lớp cơ sở dữ liệu lưu giữ cơ sở dữ liệu và đóng vai trò là điểm truy cập chính cho đường kết nối cơ bản đến dữ liệu cố định của ứng dụng.
- Thực thể dữ liệu biểu thị các bảng trong cơ sở dữ liệu của ứng dụng.
- Đối tượng truy cập dữ liệu (DAO) cung cấp các phương thức mà ứng dụng của bạn có thể dùng để truy vấn, cập nhật, chèn và xoá dữ liệu trong cơ sở dữ liệu.
Lớp cơ sở dữ liệu cung cấp cho ứng dụng của bạn các thực thể của DAO được liên kết với cơ sở dữ liệu đó. Đổi lại, ứng dụng có thể dùng DAO để truy xuất dữ liệu từ cơ sở dữ liệu dưới dạng thực thể của đối tượng thực thể dữ liệu được liên kết. Ứng dụng cũng có thể dùng các thực thể dữ liệu đã xác định để cập nhật các hàng trong bảng tương ứng hoặc tạo hàng mới để chèn dữ liệu. Hình 1 minh hoạ mối quan hệ giữa các thành phần của Room.
Triển khai mẫu
Phần này trình bày phương thức triển khai mẫu cho cơ sở dữ liệu Room với một thực thể dữ liệu và một DAO duy nhất.
Thực thể dữ liệu
Mã sau đây định nghĩa một thực thể dữ liệu User
. Mỗi thực thể của User
biểu thị một hàng trong bảng user
thuộc cơ sở dữ liệu của ứng dụng.
Kotlin
@Entity data class User( @PrimaryKey val uid: Int, @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? )
Java
@Entity public class User { @PrimaryKey public int uid; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; }
Để tìm hiểu thêm về các thực thể dữ liệu trong Room, hãy xem bài viết Xác định dữ liệu bằng các thực thể của Room.
Đối tượng truy cập dữ liệu (DAO)
Đoạn mã sau đây định nghĩa một DAO có tên UserDao
. UserDao
cung cấp các phương thức mà phần còn lại của ứng dụng sẽ dùng để tương tác với dữ liệu trong bảng user
.
Kotlin
@Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray): List<User> @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") fun findByName(first: String, last: String): User @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) }
Java
@Dao public interface UserDao { @Query("SELECT * FROM user") List<User> getAll(); @Query("SELECT * FROM user WHERE uid IN (:userIds)") List<User> loadAllByIds(int[] userIds); @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") User findByName(String first, String last); @Insert void insertAll(User... users); @Delete void delete(User user); }
Để tìm hiểu thêm về DAO, hãy xem bài viết Truy cập vào dữ liệu bằng các DAO của Room.
Cơ sở dữ liệu
Đoạn mã sau đây định nghĩa một lớp AppDatabase
để lưu giữ cơ sở dữ liệu.
AppDatabase
định nghĩa cấu hình cơ sở dữ liệu và đóng vai trò là điểm truy cập chính của ứng dụng vào dữ liệu cố định. Lớp cơ sở dữ liệu phải đáp ứng các điều kiện sau:
- Lớp phải được chú thích bằng thẻ chú thích
@Database
có chứa mảng (array)entities
liệt kê mọi thực thể dữ liệu được liên kết với cơ sở dữ liệu. - Lớp này phải là một lớp trừu tượng mở rộng
RoomDatabase
. - Đối với mỗi lớp DAO được liên kết với cơ sở dữ liệu, lớp cơ sở dữ liệu phải định nghĩa một phương thức trừu tượng không có đối số và trả về một thực thể của lớp DAO.
Kotlin
@Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
Java
@Database(entities = {User.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao(); }
Lưu ý: Nếu ứng dụng của bạn chạy trong một quy trình, thì bạn nên tuân theo mẫu thiết kế singleton khi tạo thực thể cho đối tượng AppDatabase
. Mỗi thực thể RoomDatabase
đều khá tốn kém và bạn hiếm khi cần quyền truy cập vào nhiều thực thể trong một quy trình.
Nếu ứng dụng của bạn chạy trong nhiều quy trình, hãy đưa enableMultiInstanceInvalidation()
vào lệnh gọi trình tạo cơ sở dữ liệu. Bằng cách đó, khi có thực thể của AppDatabase
trong mỗi quy trình, bạn có thể vô hiệu hoá tệp cơ sở dữ liệu dùng chung trong một quy trình và hoạt động vô hiệu hoá này sẽ tự động truyền đến các thực thể của AppDatabase
trong những quy trình khác.
Cách sử dụng
Sau khi định nghĩa thực thể dữ liệu, DAO và đối tượng cơ sở dữ liệu, bạn có thể dùng đoạn mã sau để tạo một thực thể của cơ sở dữ liệu:
Kotlin
val db = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "database-name" ).build()
Java
AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build();
Sau đó, bạn có thể dùng các phương thức trừu tượng từ AppDatabase
để lấy thực thể của DAO. Đổi lại, bạn có thể dùng các phương thức từ thực thể DAO để tương tác với cơ sở dữ liệu:
Kotlin
val userDao = db.userDao() val users: List<User> = userDao.getAll()
Java
UserDao userDao = db.userDao(); List<User> users = userDao.getAll();
Tài nguyên khác
Để tìm hiểu thêm về Room, hãy xem thêm các tài nguyên sau đây:
Mẫu
Lớp học lập trình
Blog
- 7 mẹo hay dành cho Room
- Di chuyển dần từ SQLite sang Room