Room: nó là gì, thư viện ORM và làm việc với SQLite

Tác giả: IT Sectr Đã đăng: 2026-03-12 Thời gian đọc: 10 phút

Room là thư viện ORM thuộc Android Jetpack, cung cấp một lớp trừu tượng trên SQLite để làm việc với cơ sở dữ liệu cục bộ trên Android. Theo tài liệu chính thức tại Android Developers, 2025, Room tự động tạo ra các triển khai DAO dựa trên chú thích tại thời điểm biên dịch, loại bỏ khoảng 70% mã boilerplate so với việc sử dụng trực tiếp SQLiteOpenHelper. Thư viện thực hiện xác thực truy vấn SQL tại thời điểm biên dịch, cho phép phát hiện lỗi cú pháp trước khi chạy ứng dụng trên thiết bị.

Những Điểm Chính

  • Room là thư viện ORM của Android Jetpack cung cấp lớp trừu tượng trên SQLite để lưu trữ dữ liệu cục bộ trong ứng dụng Android.
  • Ba thành phần chính: Entity (định nghĩa bảng), DAO (thao tác dữ liệu) và Database (điểm truy cập cơ sở dữ liệu).
  • Xác thực truy vấn SQL tại thời điểm biên dịch là lợi thế chính, cho phép tìm lỗi trước khi cài đặt ứng dụng.
  • Hỗ trợ tích hợp cho Flow, LiveData và RxJava để quan sát phản ứng các thay đổi trong cơ sở dữ liệu.
  • Cơ chế di chuyển cho phép cập nhật lược đồ cơ sở dữ liệu mà không làm mất dữ liệu người dùng đã lưu.

Thư viện Room ORM là gì?

Room là thư viện ORM thuộc Android Jetpack do Google tạo ra để đơn giản hóa việc làm việc với cơ sở dữ liệu SQLite cục bộ trên nền tảng Android. Nó cung cấp các chú thích để mô tả lược đồ dữ liệu và tự động tạo triển khai giao diện DAO tại thời điểm biên dịch. Không giống như sử dụng trực tiếp SQLiteOpenHelper, Room giải phóng nhà phát triển khỏi việc viết một lượng đáng kể mã boilerplate để tạo, mở và quản lý kết nối cơ sở dữ liệu.

Thư viện được giới thiệu tại Google I/O 2017 như một phần của các thành phần kiến trúc Android. Kể từ đó, Room đã trở thành tiêu chuẩn thực tế cho lưu trữ dữ liệu cục bộ, vượt qua các giải pháp như GreenDAO và Realm cho Android về mức độ phổ biến. Theo Google, thư viện được sử dụng trong hơn 60% ứng dụng được xuất bản trên Google Play làm việc với dữ liệu cục bộ trên thiết bị.

Tính năng chính là xác thực truy vấn SQL tại thời điểm biên dịch bằng bộ xử lý chú thích. Nếu nhà phát triển mắc lỗi trong lệnh SQL, ví dụ chỉ định tên cột không tồn tại, quá trình xây dựng sẽ thất bại với lỗi trước khi ứng dụng được cài đặt. Điều này khác biệt cơ bản so với cách tiếp cận SQLiteOpenHelper, nơi những lỗi như vậy chỉ được phát hiện tại thời điểm chạy, thường là trong sản phẩm.

TypeConverters cho các kiểu không chuẩn

SQLite chỉ hỗ trợ năm kiểu dữ liệu: TEXT, INTEGER, REAL, BLOB và NULL. Tuy nhiên, Java và Kotlin sử dụng các kiểu phức tạp: Date, List, Enum và đối tượng tùy chỉnh. Để lưu trữ chúng, Room cung cấp cơ chế TypeConverters — các phương thức tĩnh chuyển đổi kiểu phức tạp thành kiểu nguyên thủy mà SQLite có thể hiểu được. Ví dụ, đối tượng Date được chuyển đổi thành Long (dấu thời gian) và List<String> thành chuỗi JSON qua Gson hoặc Moshi.

kotlin
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .build()

Để khai báo bộ chuyển đổi, chỉ cần thêm chú thích @TypeConverter vào một phương thức tĩnh và chỉ định lớp chuyển đổi trong chú thích @TypeConverters ở cấp cơ sở dữ liệu. Room tự động áp dụng bộ chuyển đổi khi đọc và ghi kiểu tương ứng trong mỗi truy vấn SQL mà không cần gọi thủ công các phương thức chuyển đổi.

Kiến trúc Room: ba thành phần chính

Room bao gồm ba thành phần chính: Entity, DAO và Database. Mỗi thành phần thực hiện một vai trò được xác định chặt chẽ và được chú thích bằng chú thích tương ứng. Cùng nhau, chúng tạo thành một lớp truy cập dữ liệu hoàn chỉnh giúp cách ly logic kinh doanh của ứng dụng khỏi chi tiết triển khai SQLite.

Entity — Bảng cơ sở dữ liệu

Entity là một lớp dữ liệu mô tả cấu trúc của một bảng trong cơ sở dữ liệu. Mỗi trường của lớp tương ứng với một cột của bảng và mỗi hàng trong cơ sở dữ liệu tương ứng với một thể hiện của lớp. Chú thích @Entity cho Room biết rằng lớp đó là một bảng. Trường có chú thích @PrimaryKey xác định khóa chính, có thể tự động tăng hoặc tổng hợp. @ForeignKey được sử dụng cho mối quan hệ giữa các bảng, đảm bảo tính toàn vẹn dữ liệu ở cấp cơ sở dữ liệu.

kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "full_name")
    val name: String,
    val age: Int,
    val email: String
)

DAO — Thao tác dữ liệu

DAO (Data Access Object) là một giao diện hoặc lớp trừu tượng khai báo các thao tác làm việc với dữ liệu: chèn, đọc, cập nhật và xóa. Mỗi thao tác được chú thích bằng @Insert, @Query, @Update hoặc @Delete. Room tự động tạo triển khai của giao diện này tại thời điểm biên dịch. Chú thích @Query đặc biệt có giá trị — nó chấp nhận một truy vấn SQL dưới dạng chuỗi và xác thực tính đúng đắn của nó tại thời điểm xây dựng.

kotlin
@Dao
interface UserDao {
    @Insert
    suspend fun insert(user: User): Long

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?

    @Query("SELECT * FROM users")
    fun getAllUsers(): Flow<List<User>>

    @Delete
    suspend fun delete(user: User)
}

Database — Điểm truy cập

Database là một lớp trừu tượng mở rộng RoomDatabase, đóng vai trò là điểm truy cập vào cơ sở dữ liệu. Nó chứa danh sách tất cả các Entity và cung cấp các phương thức trừu tượng để lấy DAO. Lớp được chú thích bằng @Database, chỉ định phiên bản lược đồ và danh sách thực thể. Phiên bản cơ sở dữ liệu được tạo qua Room.databaseBuilder với ngữ cảnh ứng dụng, tên tệp và lớp Database.

Cách Room hoạt động với SQLite bên trong

Room không thay thế SQLite, mà hoạt động trên nó như một lớp trừu tượng. Kiến trúc bên trong bao gồm bộ xử lý chú thích, trình tạo mã và nhóm kết nối. Tại thời điểm biên dịch, bộ xử lý chú thích phân tích các lớp Entity, DAO và Database, sau đó tạo ra các lớp triển khai với hậu tố _Impl. Tất cả các lớp được tạo được đặt trong gói xây dựng và không hiển thị trực tiếp cho nhà phát triển.

Tạo mã tại thời điểm biên dịch là cơ chế trung tâm của Room. Cho mỗi giao diện DAO, một lớp với triển khai đầy đủ của tất cả các phương thức được chú thích được tạo ra. Các truy vấn SQL từ chú thích @Query được xác thực về tính đúng đắn: bộ xử lý đối sánh tên cột với trường Entity và kiểm tra cú pháp SQL. Nếu tìm thấy lỗi, quá trình xây dựng bị gián đoạn với thông báo rõ ràng. Điều này không thể thực hiện khi sử dụng SQLiteOpenHelper thô, nơi lỗi chỉ xuất hiện tại thời điểm chạy.

Tạo mã tại thời điểm biên dịch

Quy trình tạo bao gồm ba giai đoạn. Đầu tiên — xác thực lược đồ: bộ xử lý kiểm tra rằng tất cả các lớp được liệt kê trong @Database là Entity hợp lệ. Thứ hai — tạo thân DAO: cho mỗi phương thức, một triển khai được tạo bằng cách sử dụng đối tượng nội bộ RoomSQLiteQuery thực thi các truy vấn đã chuẩn bị. Thứ ba — tạo lớp Database_Impl, xử lý việc tạo và mở cơ sở dữ liệu, cũng như khởi tạo tất cả các đối tượng DAO.

kotlin
class UserDao_Impl(private val __db: RoomDatabase) : UserDao {
    private val __insertionAdapter = __db
        .createInsertionAdapter(User::class, 0)

    override suspend fun insert(user: User): Long {
        __db.assertNotSuspendingTransaction()
        return __db.runInTransaction {
            __insertionAdapter.insertAndReturnId(user)
        }
    }
}

Room không tạo nhóm luồng riêng cho các thao tác cơ sở dữ liệu. Theo mặc định, các truy vấn được thực thi trong luồng gọi với một hạn chế: đọc và ghi chặn luồng. Đối với công việc không đồng bộ, Room tích hợp với coroutines Kotlin qua các hàm suspend, với LiveData qua giá trị trả về và với Flow qua các trình bao bọc phản ứng. Điều này mang lại cho nhà phát triển sự linh hoạt để chọn giải pháp kiến trúc cho một tác vụ cụ thể.

Ví dụ sử dụng Room trong ứng dụng Android

Hãy xem một ví dụ thực tế về việc tạo ứng dụng ghi chú sử dụng Room. Ứng dụng chứa một bảng Note với các trường id, title, content và timestamp. Người dùng có thể thêm, xem và xóa ghi chú. Coroutines được sử dụng cho các thao tác không đồng bộ.

Thiết lập phụ thuộc Gradle

Để tích hợp Room vào dự án Android, hãy thêm phụ thuộc vào tệp build.gradle cấp mô-đun. Room yêu cầu ba thành phần: thư viện thời gian chạy, bộ xử lý chú thích kapt và hỗ trợ coroutines tùy chọn. Phiên bản thư viện được chỉ định trong biến room_version để dễ dàng cập nhật. Bắt đầu từ Room 2.4.0, KSP được hỗ trợ như một thay thế cho kapt với tốc độ xây dựng nhanh hơn.

groovy
dependencies {
    def room_version = "2.6.1"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    // Tùy chọn: kiểm thử
    testImplementation "androidx.room:room-testing:$room_version"
}

Sau khi thiết lập phụ thuộc, hãy tạo ba tệp: Entity Note, giao diện NoteDao và lớp AppDatabase. Entity Note chứa các trường với chú thích @PrimaryKey và @ColumnInfo. DAO cung cấp các phương thức để chèn, lấy danh sách và xóa. Database liên kết Entity và DAO qua chú thích @Database.

kotlin
@Entity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val title: String,
    val content: String,
    @ColumnInfo(name = "created_at")
    val timestamp: Long = System.currentTimeMillis()
)

@Dao
interface NoteDao {
    @Insert
    suspend fun insert(note: Note)

    @Query("SELECT * FROM notes ORDER BY created_at DESC")
    fun getAllNotes(): Flow<List<Note>>

    @Delete
    suspend fun delete(note: Note)
}

Tệp AppDatabase được khai báo là một lớp trừu tượng mở rộng RoomDatabase. Chú thích @Database chỉ định tất cả các Entity cho phiên bản hiện tại và số phiên bản lược đồ. Để lấy một phiên bản, mẫu singleton được sử dụng qua phương thức build của Room.databaseBuilder với ngữ cảnh ứng dụng. Lưu vào bộ nhớ đệm phiên bản cơ sở dữ liệu ngăn chặn nhiều lần tạo có thể dẫn đến rò rỉ bộ nhớ.

Di chuyển cơ sở dữ liệu trong Room

Di chuyển trong Room là cơ chế thay đổi lược đồ cơ sở dữ liệu khi cập nhật ứng dụng mà không làm mất dữ liệu hiện có. Khi người dùng cài đặt phiên bản mới với các Entity đã được sửa đổi, Room phát hiện sự không khớp phiên bản và thực thi các bước di chuyển được chỉ định. Nếu không có di chuyển, cơ sở dữ liệu sẽ bị xóa và tạo lại, dẫn đến mất tất cả dữ liệu đã lưu của người dùng.

Di chuyển được mô tả bởi lớp Migration, nhận phiên bản bắt đầu và kết thúc của cơ sở dữ liệu. Bên trong phương thức migrate, một truy vấn SQL ALTER TABLE hoặc CREATE TABLE được thực thi để thay đổi lược đồ. Room không thể tự động phát hiện thay đổi lược đồ — nhà phát triển phải viết di chuyển thủ công cho mỗi thay đổi Entity. Bắt đầu từ Room 2.4.0, tính năng thử nghiệm autoMigrations có sẵn để tạo di chuyển tự động.

Di chuyển tự động với autoMigrations

Tính năng autoMigrations cho phép Room tự động tạo di chuyển dựa trên sự khác biệt giữa các phiên bản Entity. Để sử dụng, chỉ cần thêm chú thích @AutoMigration vào @Database và kích hoạt xuất lược đồ sang JSON. Room so sánh lược đồ của các phiên bản liền kề và tạo các truy vấn ALTER cần thiết. Tuy nhiên, autoMigrations chỉ hỗ trợ các thay đổi tương thích ngược: thêm cột, tạo chỉ mục và thay đổi kiểu với các chuyển đổi tương thích.

kotlin
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL(
            "ALTER TABLE users ADD COLUMN phone TEXT"
        )
    }
}

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .addMigrations(MIGRATION_1_2)
    .build()

Đối với các thay đổi phức tạp, như đổi tên cột hoặc hợp nhất bảng, cần di chuyển thủ công sử dụng bảng trung gian. Một kịch bản điển hình: tạo bảng tạm thời với lược đồ cũ, sao chép dữ liệu từ bảng cũ sang bảng mới với các chuyển đổi, xóa bảng cũ và đổi tên bảng tạm thời. Room đảm bảo rằng tất cả các di chuyển được thực thi trong một giao dịch duy nhất và nếu xảy ra lỗi, các thay đổi sẽ được hoàn tác hoàn toàn.

Câu Hỏi Thường Gặp

Room khác SQLiteOpenHelper như thế nào?

Room cung cấp một sự trừu tượng ORM với chú thích và xác thực SQL tại thời điểm biên dịch, trong khi SQLiteOpenHelper yêu cầu viết thủ công tất cả các truy vấn và quản lý kết nối. Room tự động tạo mã cho các thao tác CRUD và tích hợp với các thành phần kiến trúc Android, bao gồm LiveData và Flow.

Room hỗ trợ những kiểu dữ liệu nào?

Room hỗ trợ tất cả các kiểu nguyên thủy Java: Int, Long, Boolean, Float, Double, cũng như String, ByteArray và Date. Đối với các kiểu phức tạp như List hoặc Enum, TypeConverters được sử dụng — các phương thức chuyển đổi tĩnh chuyển đổi các kiểu không chuẩn thành định dạng tương thích với SQLite.

Có thể sử dụng Room mà không cần coroutines không?

Có, Room hỗ trợ các cuộc gọi đồng bộ mà không cần coroutines, nhưng chúng chặn luồng mà chúng thực thi. Đối với công việc không đồng bộ, bạn có thể sử dụng LiveData hoặc RxJava thay vì coroutines. Google khuyến nghị sử dụng coroutines như phương pháp chính để truy cập dữ liệu không đồng bộ trong các dự án mới.

Điều gì xảy ra nếu thiếu di chuyển?

Nếu Room phát hiện sự không khớp phiên bản cơ sở dữ liệu và không tìm thấy di chuyển phù hợp, theo mặc định nó ném ra IllegalStateException với mô tả lỗi. Nhà phát triển có thể ghi đè hành vi này bằng phương thức fallbackToDestructiveMigration, phương thức này sẽ xóa cơ sở dữ liệu hiện tại và tạo cơ sở dữ liệu mới, làm mất tất cả dữ liệu.

Room xử lý mối quan hệ giữa các bảng như thế nào?

Room hỗ trợ mối quan hệ thông qua các đối tượng lồng nhau với chú thích @Embedded và thông qua các lớp quan hệ với chú thích @Relation. Đối với các truy vấn phức tạp liên quan đến kết nối bảng, các lớp POJO tùy chỉnh được sử dụng, các trường của chúng được điền từ kết quả của @Query với câu lệnh SQL JOIN.

Tổng Kết

  • Room là thư viện ORM của Android Jetpack tạo một lớp trừu tượng trên SQLite để lưu trữ dữ liệu thuận tiện trên thiết bị.
  • Kiến trúc dựa trên ba thành phần: Entity (lược đồ bảng), DAO (thao tác) và Database (điểm truy cập).
  • Xác thực truy vấn SQL tại thời điểm biên dịch là lợi thế chính, loại bỏ lỗi thời gian chạy trong truy vấn.
  • Hỗ trợ tích hợp cho Flow, LiveData và RxJava cho phép xây dựng kiến trúc phản ứng với cập nhật giao diện tự động khi dữ liệu thay đổi.
  • Di chuyển trong Room đảm bảo cập nhật lược đồ cơ sở dữ liệu liền mạch mà không làm mất thông tin người dùng đã lưu.
  • Thư viện tích hợp với coroutines Kotlin qua hàm suspend, đơn giản hóa công việc không đồng bộ với dữ liệu.
  • Đối với các dự án mới, Room là giải pháp được Google chính thức khuyến nghị cho lưu trữ dữ liệu cục bộ trên Android.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm