Chọn loại mối quan hệ giữa các đối tượng

Do SQLite là một cơ sở dữ liệu quan hệ, nên bạn có thể xác định mối quan hệ giữa các thực thể. Tuy nhiên, trong khi hầu hết các thư viện ánh xạ quan hệ đối tượng cho phép các đối tượng thực thể tham chiếu lẫn nhau thì Room cấm điều này một cách rõ ràng. Để tìm hiểu về lý do kỹ thuật đằng sau quyết định này, hãy xem phần Tìm hiểu lý do Room không cho phép tham chiếu đối tượng.

Các loại mối quan hệ

Room hỗ trợ các loại mối quan hệ sau:

  • Một-một: Biểu thị mối quan hệ trong đó một thực thể duy nhất có liên quan đến một thực thể duy nhất khác.
  • Một-nhiều: Biểu thị mối quan hệ trong đó một thực thể có thể liên quan đến nhiều thực thể thuộc một loại khác.
  • Nhiều-nhiều: Biểu thị mối quan hệ trong đó nhiều thực thể thuộc một loại có thể liên quan đến nhiều thực thể thuộc một loại khác. Việc này thường yêu cầu một bảng kết hợp.
  • Mối quan hệ lồng nhau bằng cách sử dụng các đối tượng được nhúng: Thể hiện mối quan hệ trong đó một thực thể chứa một thực thể khác dưới dạng một thuộc tính và thực thể lồng nhau này có thể chứa thêm các thực thể khác. Thao tác này sử dụng chú giải @Embedded.

Chọn một trong hai phương pháp

Trong Room, có hai cách để xác định và truy vấn mối quan hệ giữa các thực thể. Bạn có thể sử dụng một trong hai cách sau:

  • Một lớp dữ liệu trung gian có các đối tượng được nhúng, hoặc
  • Một hàm truy vấn quan hệ có kiểu dữ liệu trả về nhiều bản đồ.

Nếu không có lý do cụ thể để sử dụng lớp dữ liệu trung gian, bạn nên sử dụng phương pháp sử dụng kiểu dữ liệu trả về đa ánh xạ. Để tìm hiểu thêm về phương pháp này, hãy xem phần Trả về cấu trúc đa ánh xạ.

Phương pháp lớp dữ liệu trung gian giúp bạn không phải viết các truy vấn SQL phức tạp, nhưng cũng có thể làm tăng tính phức tạp của mã vì phương pháp này yêu cầu các lớp dữ liệu bổ sung. Tóm lại, phương pháp kiểu dữ liệu trả về đa ánh xạ đòi hỏi các truy vấn SQL làm nhiều việc hơn; còn phương pháp lớp dữ liệu trung gian sẽ yêu cầu mã làm nhiều việc hơn.

Sử dụng phương pháp lớp dữ liệu trung gian

Trong cách tiếp cận lớp dữ liệu trung gian, bạn xác định một lớp dữ liệu giúp mô hình hoá mối quan hệ giữa các thực thể Room của bạn. Lớp dữ liệu này duy trì ghép nối giữa các bản sao của một thực thể và các bản sao của một thực thể khác dưới dạng đối tượng được nhúng. Sau đó, các hàm truy vấn có thể trả về các thực thể của lớp dữ liệu này để sử dụng trong ứng dụng.

Ví dụ: bạn có thể xác định lớp dữ liệu UserBook để biểu thị người dùng thư viện với cuốn sách cụ thể được cho mượn và xác định hàm truy vấn để truy xuất danh sách các thực thể UserBook từ cơ sở dữ liệu:

@Dao
interface UserBookDao {
    @Query(
        """
        SELECT user.name AS userName, book.name AS bookName
        FROM user JOIN book ON user.id = book.user_id
        """
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

data class UserBook(val userName: String, val bookName: String)

Sử dụng phương pháp kiểu dữ liệu trả về nhiều bản đồ

Trong phương pháp tiếp cận loại dữ liệu trả về nhiều bản đồ, bạn không cần phải xác định thêm lớp dữ liệu nào cả. Thay vào đó, bạn xác định kiểu dữ liệu trả về multimap cho hàm dựa trên cấu trúc bản đồ bạn muốn và trực tiếp xác định mối quan hệ giữa các thực thể trong truy vấn SQL.

Ví dụ: hàm truy vấn sau đây trả về mối liên kết của các thực thể UserBook để biểu diễn người dùng thư viện với cuốn sách cụ thể được cho mượn:

@Query(
    """
    SELECT *
    FROM user JOIN book ON user.id = book.user_id
    """
)
suspend fun loadUserAndBookNames(): Map<User, List<Book>>

Với kiểu dữ liệu trả về nhiều bản đồ, bạn cũng có thể truy vấn các mối quan hệ một-một không liên quan đến một thực thể khác. Hàm truy vấn sau đây trả về mối liên kết của User và số lượng sách mà họ đã mượn bằng chú giải @MapColumn:

@Query(
    """
    SELECT user.*, COUNT(book.id) AS book_count
    FROM user LEFT JOIN book ON user.id = book.user_id
    GROUP BY user.id
    """
)
suspend fun loadUserAndBookCount(): Map<User, @MapColumn(columnName = "book_count") Int>

Tạo đối tượng nhúng

Đôi khi, bạn muốn biểu thị một thực thể hoặc đối tượng dữ liệu dưới dạng một tổng thể ràng buộc trong logic cơ sở dữ liệu, ngay cả khi đối tượng đó chứa nhiều thuộc tính. Trong những trường hợp này, hãy sử dụng chú giải @Embedded để phân rã một đối tượng thành các thuộc tính phụ trong bảng. Sau đó, bạn có thể truy vấn các thuộc tính được nhúng giống như với các cột khác.

Ví dụ: lớp User có thể bao gồm một thuộc tính Address đại diện cho thành phần của các thuộc tính street, city, statepostCode. Để lưu trữ các cột đã được phân rã riêng rẽ trong bảng, hãy chú giải thuộc tính Address trong lớp User bằng @Embedded. Đoạn mã sau đây cho thấy chế độ thiết lập này:

data class Address(
    val street: String?,
    val state: String?,
    val city: String?,
    @ColumnInfo(name = "post_code") val postCode: Int
)

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String,
    @Embedded val address: Address?
)

Khi này, bảng biểu diễn đối tượng User chứa các cột có tên như sau: id, firstName, street, state, citypost_code.

Nếu một thực thể có nhiều thuộc tính được nhúng cùng loại, thì bạn có thể khiến mỗi cột là duy nhất bằng cách đặt thuộc tính prefix. Sau đó, Room sẽ thêm giá trị được cung cấp vào đầu tên mỗi cột trong đối tượng được nhúng.