Wybieranie typów relacji między obiektami

SQLite to baza danych relacyjnych, więc możesz definiować relacje między obiektami. Chociaż większość bibliotek mapowania obiektów do bazy danych pozwala na wzajemne odwoływanie się obiektów, Room wyraźnie tego zabrania. Więcej informacji o technicznych aspektach tej decyzji znajdziesz w artykule (ang.) Dlaczego Room nie zezwala na odwołania do obiektów.

Typy relacji

Pokój obsługuje te typy relacji:

  • Jeden do jednego: reprezentuje relację, w której jeden element jest powiązany z innym pojedynczym elementem.
  • Jeden do wielu: przedstawia relację, w której pojedyncza entność może być powiązana z wieloma imiennikami innego typu.
  • Wiele do wielu: reprezentuje relację, w której wiele elementów jednego typu może być powiązanych z wieloma elementami innego typu. Zwykle wymaga to użycia tabeli pośredniej.
  • Ustrukturyzowane relacje (z wykorzystaniem obiektów wbudowanych): reprezentują relację, w której jeden element zawiera inny element jako pole, a ten element może zawierać inne elementy. Używa adnotacji @Embedded.

Wybór jednego z 2 podejść

W Room można zdefiniować relację między elementami i wysyłać zapytania o nią na 2 sposoby. Możesz użyć:

  • pośrednia klasa danych z wbudowanymi obiektami,
  • Relacyjna metoda zapytania z typem zwracania multimap.

Jeśli nie masz konkretnego powodu, dla którego warto używać pośrednich klas danych, zalecamy stosowanie typu zwracanego mapy wielomapowej. Więcej informacji o tym podejściu znajdziesz w artykule Zwracanie mapy wielopoziomowej.

Podejście z klasą danych pośrednich pozwala uniknąć pisania skomplikowanych zapytań SQL, ale może też zwiększyć złożoność kodu, ponieważ wymaga dodatkowych klas danych. Krótko mówiąc, podejście z typem zwracania multimap wymaga od zapytań SQL większego nakładu pracy, a podejście z klasą danych pośrednich – od kodu.

Zastosowanie podejścia z klasą danych pośrednich

W podejściu z klasą danych pośrednich definiujesz klasę danych, która modeluje relację między encji Pokój. Ta klasa danych zawiera pary instancji jednego elementu i instancji innego elementu jako osadzone obiekty. Następnie metody zapytań mogą zwracać instancje tej klasy danych do użycia w aplikacji.

Możesz na przykład zdefiniować klasę danych UserBook, aby reprezentować użytkowników biblioteki z wypożyczonymi książkami, i zdefiniować metodę zapytania, aby pobrać z bazy danych listę wystąpień UserBook:

Kotlin

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

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

Java

@Dao
public interface UserBookDao {
   @Query("SELECT user.name AS userName, book.name AS bookName " +
          "FROM user, book " +
          "WHERE user.id = book.user_id")
   public LiveData<List<UserBook>> loadUserAndBookNames();
}

public class UserBook {
    public String userName;
    public String bookName;
}

Używanie podejścia z wieloma typami map zwrotnych

W podejściu z wieloznacznikiem typu mapy nie musisz definiować dodatkowych klas danych. Zamiast tego zdefiniuj typ zwracany multimap dla swojej metody na podstawie wybranej struktury mapy i określ relacje między elementami bezpośrednio w zapytaniu SQL.

Na przykład metoda zapytania zwraca mapowanie instancji UserBook reprezentujących użytkowników biblioteki z wypożyczonymi konkretnymi książkami:

Kotlin

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

Java

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

Tworzenie obiektów umieszczanych

Czasami w logice bazy danych chcesz przedstawić element lub obiekt danych jako spójną całość, nawet jeśli obiekt zawiera kilka pól. W takich sytuacjach możesz użyć adnotacji @Embedded, aby reprezentować obiekt, który chcesz rozłożyć na podpola w tabeli. Następnie możesz wysyłać zapytania do tych pól tak samo jak do innych kolumn.

Na przykład klasa User może zawierać pole typu Address, które reprezentuje kompozycję pól o nazwach street, city, statepostCode. Aby przechowywać złożone kolumny oddzielnie w tabeli, dodaj pole Address. Powinien on pojawić się w klasie User z adnotacją @Embedded. Poniżej znajduje się fragment kodu, który to pokazuje:

Kotlin

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?
)

Java

public class Address {
    public String street;
    public String state;
    public String city;

    @ColumnInfo(name = "post_code") public int postCode;
}

@Entity
public class User {
    @PrimaryKey public int id;

    public String firstName;

    @Embedded public Address address;
}

Tabela reprezentująca obiekt User zawiera kolumny o tych nazwach: id, firstName, street, state, citypost_code.

Jeśli element ma wiele osadzonych pól tego samego typu, możesz zachować unikalność każdej kolumny, ustawiając właściwość prefix. Następnie Room dodaje podawaną wartość do początku nazwy każdej kolumny w osadzonym obiekcie.

Dodatkowe materiały

Więcej informacji o definiowaniu relacji między elementami w pokoju znajdziesz w tych dodatkowych materiałach.

Filmy

Blogi