Nesneler arasındaki ilişkileri tanımlama

SQLite ilişkisel bir veritabanı olduğundan varlıklar arasında ilişki tanımlayabilirsiniz. Ancak çoğu nesne ilişkisel eşleme kitaplığı varlık nesnelerinin birbirine referans vermesine izin verse de, Room bunu açıkça yasaklar. Bu kararın temelinde yatan teknik neden hakkında bilgi edinmek için Room'un nesne referanslarına neden izin vermediğini anlama bölümüne bakın.

İki olası yaklaşım

Odada, varlıklar arasındaki ilişkiyi tanımlamanın ve sorgulamanın iki yolu vardır: yerleştirilmiş nesneler içeren bir ara veri sınıfı veya çoklu eşleme dönüş türüne sahip ilişkisel sorgu yöntemi kullanma.

Orta düzey veri sınıfı

Orta düzey veri sınıfı yaklaşımında Oda varlıklarınız arasındaki ilişkiyi modelleyen bir veri sınıfı tanımlarsınız. Bu veri sınıfı, bir varlığın örnekleri ile başka bir varlığın örnekleri arasındaki eşleşmeleri yerleştirilmiş nesne olarak tutar. Daha sonra sorgu yöntemleriniz, uygulamanızda kullanılması için bu veri sınıfının örneklerini döndürebilir.

Örneğin, belirli kitapları teslim eden kitaplık kullanıcılarını temsil edecek bir UserBook veri sınıfı tanımlayabilir ve veritabanından UserBook örneklerinin listesini almak için bir sorgu yöntemi tanımlayabilirsiniz:

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;
}

Çoklu harita döndürme türleri

Çoklu harita dönüş türü yaklaşımında ek veri sınıfı tanımlamanız gerekmez. Bunun yerine, yönteminiz için istediğiniz harita yapısına göre bir çoklu eşleme döndürme türü ve varlıklarınız arasındaki ilişkiyi doğrudan SQL sorgunuzda tanımlarsınız.

Örneğin aşağıdaki sorgu yöntemi, belirli kitaplar için ödeme yapmış olan kitaplık kullanıcılarını temsil etmek için User ve Book örneklerinin eşlemesini döndürür:

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();

Yaklaşım belirleyin

Room, bu yaklaşımların her ikisini de desteklediğinden, uygulamanız için en uygun yaklaşımı kullanabilirsiniz. Bu bölümde, bu yaklaşımlardan birini tercih etmenizin nedenlerinden bazıları anlatılmaktadır.

Orta düzey veri sınıfı yaklaşımı, karmaşık SQL sorguları yazmaktan kaçınmanızı sağlar. Ancak bu yaklaşım, gerektirdiği ek veri sınıfları nedeniyle daha fazla kod karmaşıklığına da neden olabilir. Kısacası, çoklu eşleme dönüş türü yaklaşımı, SQL sorgularınızın daha fazla iş yapmasını gerektirir. Ara veri sınıfı yaklaşımı ise kodunuzun daha fazla iş yapmasını gerektirir.

Ara veri sınıflarını kullanmak için belirli bir nedeniniz yoksa çoklu eşleme döndürme türü yaklaşımını kullanmanızı öneririz. Bu yaklaşım hakkında daha fazla bilgi edinmek için Çoklu harita döndürme konusuna bakın.

Bu kılavuzun geri kalanında, orta düzey veri sınıfı yaklaşımı kullanılarak ilişkilerin nasıl tanımlanacağı gösterilmektedir.

Yerleştirilmiş nesneler oluşturma

Bazen, bir varlığı veya veri nesnesini, nesne birkaç alan içerse bile veritabanı mantığınızda tutarlı bir bütün olarak ifade etmek isteyebilirsiniz. Bu tür durumlarda, tablodaki alt alanlarına ayırmak istediğiniz bir nesneyi temsil etmek için @Embedded ek açıklamasını kullanabilirsiniz. Ardından, yerleştirilmiş alanları diğer bağımsız sütunlar için yaptığınız gibi sorgulayabilirsiniz.

Örneğin User sınıfınız, street, city, state ve postCode adlı alanların bileşimini temsil eden Address türünde bir alan içerebilir. Oluşturulan sütunları tabloda ayrı olarak depolamak için aşağıdaki kod snippet'inde gösterildiği gibi User sınıfına @Embedded ile not eklenmiş bir Address alanı ekleyin:

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;
}

Ardından bir User nesnesini temsil eden tablo şu adlara sahip sütunlar içerir: id, firstName, street, state, city ve post_code.

Bir varlıkta aynı türde birden çok yerleşik alan varsa prefix özelliğini ayarlayarak her bir sütunun benzersiz kalmasını sağlayabilirsiniz. Daha sonra oda, sağlanan değeri yerleştirilmiş nesnedeki her sütun adının başına ekler.

Bire-bir ilişkileri tanımlama

İki varlık arasındaki bire bir ilişki, üst varlığın her örneğinin alt varlığın tam olarak bir örneğine karşılık geldiği bir ilişkidir. Bunun tam tersi de geçerlidir.

Örneğin, kullanıcının şarkı kitaplığına sahip olduğu bir müzik yayın uygulamasını düşünün. Her kullanıcının yalnızca bir kitaplığı vardır ve her kitaplık tam olarak bir kullanıcıya karşılık gelir. Bu nedenle, User varlığı ile Library varlığı arasında bire bir ilişki vardır.

Bire bir ilişki tanımlamak amacıyla önce iki öğenizin her biri için bir sınıf oluşturun. Varlıklardan biri, diğer varlığın birincil anahtarına referans olan bir değişken içermelidir.

Kotlin

@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)

@Entity
data class Library(
    @PrimaryKey val libraryId: Long,
    val userOwnerId: Long
)

Java

@Entity
public class User {
    @PrimaryKey public long userId;
    public String name;
    public int age;
}

@Entity
public class Library {
    @PrimaryKey public long libraryId;
    public long userOwnerId;
}

Kullanıcı listesini ve ilgili kitaplıkları sorgulamak için öncelikle iki varlık arasındaki bire bir ilişkiyi modellemeniz gerekir. Bunu yapmak için her örneğin üst varlığın ve karşılık gelen alt varlığın bir örneğini bulunduran yeni bir veri sınıfı oluşturun. @Relation notunu alt varlık örneğine ekleyin. parentColumn ek açıklamayı üst varlığın birincil anahtar sütununun adı, entityColumn ise üst varlığın birincil anahtarına referans veren alt varlığın sütununun adı olarak ayarlanır.

Kotlin

data class UserAndLibrary(
    @Embedded val user: User,
    @Relation(
         parentColumn = "userId",
         entityColumn = "userOwnerId"
    )
    val library: Library
)

Java

public class UserAndLibrary {
    @Embedded public User user;
    @Relation(
         parentColumn = "userId",
         entityColumn = "userOwnerId"
    )
    public Library library;
}

Son olarak, DAO sınıfına, üst varlıkla alt varlığı eşleyen veri sınıfının tüm örneklerini döndüren bir yöntem ekleyin. Bu yöntem için Room'un iki sorgu çalıştırılması gerekir. Bu nedenle, tüm işlemin atom olarak gerçekleştirilmesi için bu yönteme @Transaction ek açıklamasını ekleyin.

Kotlin

@Transaction
@Query("SELECT * FROM User")
fun getUsersAndLibraries(): List<UserAndLibrary>

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserAndLibrary> getUsersAndLibraries();

Bire-çok ilişkiler tanımlama

İki varlık arasındaki bire çok ilişki, üst varlığın her örneğinin alt varlığın sıfır veya daha fazla örneğine karşılık geldiği, ancak alt varlığın her örneğinin yalnızca tam olarak bir üst varlık örneğine karşılık geldiği bir ilişkidir.

Müzik yayın uygulaması örneğinde, kullanıcının şarkılarını şarkı listeleri halinde düzenleyebildiğini varsayalım. Her kullanıcı istediği kadar oynatma listesi oluşturabilir, ancak her oynatma listesi tam olarak bir kullanıcı tarafından oluşturulur. Bu nedenle, User varlığı ile Playlist varlığı arasında bire-çok ilişki vardır.

Bire-çok şeklinde bir ilişki tanımlamak için önce iki varlık için bir sınıf oluşturun. Bire-bir ilişkide olduğu gibi alt varlık, ana varlığın birincil anahtarına başvuru olan bir değişken içermelidir.

Kotlin

@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val userCreatorId: Long,
    val playlistName: String
)

Java

@Entity
public class User {
    @PrimaryKey public long userId;
    public String name;
    public int age;
}

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public long userCreatorId;
    public String playlistName;
}

Kullanıcı listesini ve ilgili oynatma listelerini sorgulamak için ilk olarak iki varlık arasındaki bire-çok ilişkiyi modellemeniz gerekir. Bunu yapmak için her örneğin üst varlığın bir örneğini ve karşılık gelen tüm alt varlık örneklerinin listesini barındıran yeni bir veri sınıfı oluşturun. @Relation notunu alt varlık örneğine ekleyin. parentColumn ek açıklamayı üst varlığın birincil anahtar sütununun adı, entityColumn ise üst varlığın birincil anahtarına referans veren alt varlığın sütununun adı olarak ayarlanır.

Kotlin

data class UserWithPlaylists(
    @Embedded val user: User,
    @Relation(
          parentColumn = "userId",
          entityColumn = "userCreatorId"
    )
    val playlists: List<Playlist>
)

Java

public class UserWithPlaylists {
    @Embedded public User user;
    @Relation(
         parentColumn = "userId",
         entityColumn = "userCreatorId"
    )
    public List<Playlist> playlists;
}

Son olarak, DAO sınıfına, üst varlıkla alt varlığı eşleyen veri sınıfının tüm örneklerini döndüren bir yöntem ekleyin. Bu yöntem için Room'un iki sorgu çalıştırılması gerekir. Bu nedenle, tüm işlemin atom olarak gerçekleştirilmesi için bu yönteme @Transaction ek açıklamasını ekleyin.

Kotlin

@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylists(): List<UserWithPlaylists>

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylists> getUsersWithPlaylists();

Çoka-çok ilişkileri tanımlama

İki varlık arasındaki çoka-çok ilişki, üst varlığın her bir örneğinin alt varlığın sıfır veya daha fazla örneğine karşılık geldiği bir ilişkidir. Bunun tersi de geçerlidir.

Müzik yayın uygulaması örneğinde, kullanıcı tanımlı şarkı listelerindeki şarkıları düşünün. Her şarkı listesi birçok şarkı içerebilir ve her şarkı, birçok farklı şarkı listesinin parçası olabilir. Bu nedenle, Playlist varlığı ile Song varlığı arasında çoka-çok ilişki vardır.

Çoka-çok bir ilişki tanımlamak için önce iki öğenizin her biri için bir sınıf oluşturun. Çoka-çok ilişkiler, genellikle alt varlıkta ana varlığa referans olmadığı için diğer ilişki türlerinden farklıdır. Bunun yerine, iki varlık arasında bir ilişkisel varlığı veya çapraz referans tablosunu temsil edecek bir üçüncü sınıf oluşturun. Çapraz referans tablosunda, tabloda temsil edilen çoka-çok ilişkideki her varlıktan birincil anahtara ait sütunlar olmalıdır. Bu örnekte, çapraz referans tablosundaki her bir satır, bir Playlist örneğinin ve referans verilen şarkının referans verilen şarkı listesine dahil edildiği bir Song örneğinin çiftine karşılık gelir.

Kotlin

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public String playlistName;
}

@Entity
public class Song {
    @PrimaryKey public long songId;
    public String songName;
    public String artist;
}

@Entity(primaryKeys = {"playlistId", "songId"})
public class PlaylistSongCrossRef {
    public long playlistId;
    public long songId;
}

Sonraki adım, bu ilgili varlıkları nasıl sorgulamak istediğinize bağlıdır.

  • Şarkı listelerini ve her şarkı listesine karşılık gelen şarkıların listesini sorgulamak isterseniz tek bir Playlist nesnesi ve oynatma listesinde bulunan tüm Song nesnelerinin listesini içeren yeni bir veri sınıfı oluşturun.
  • Şarkıları ve her birine karşılık gelen şarkı listelerinin listesini sorgulamak isterseniz tek bir Song nesnesi ve şarkının dahil olduğu tüm Playlist nesnelerinin bir listesini içeren yeni bir veri sınıfı oluşturun.

Her iki durumda da Playlist varlığı ile Song varlığı arasındaki ilişkiyi sağlayan çapraz referans varlığını tanımlamak için, bu sınıfların her birinde @Relation ek açıklamasında bulunan associateBy özelliğini kullanarak varlıklar arasındaki ilişkiyi modelleyin.

Kotlin

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

data class SongWithPlaylists(
    @Embedded val song: Song,
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val playlists: List<Playlist>
)

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Song> songs;
}

public class SongWithPlaylists {
    @Embedded public Song song;
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Playlist> playlists;
}

Son olarak, uygulamanızın ihtiyaç duyduğu sorgu işlevini açığa çıkarmak için DAO sınıfına bir yöntem ekleyin.

  • getPlaylistsWithSongs: Bu yöntem, veritabanını sorgular ve elde edilen tüm PlaylistWithSongs nesnelerini döndürür.
  • getSongsWithPlaylists: Bu yöntem, veritabanını sorgular ve elde edilen tüm SongWithPlaylists nesnelerini döndürür.

Bu yöntemlerin her biri, Room'un iki sorgu çalıştırmasını gerektirir. Bu nedenle, tüm işlemin atom olarak gerçekleştirilmesi için @Transaction ek açıklamasını her iki yönteme de ekleyin.

Kotlin

@Transaction
@Query("SELECT * FROM Playlist")
fun getPlaylistsWithSongs(): List<PlaylistWithSongs>

@Transaction
@Query("SELECT * FROM Song")
fun getSongsWithPlaylists(): List<SongWithPlaylists>

Java

@Transaction
@Query("SELECT * FROM Playlist")
public List<PlaylistWithSongs> getPlaylistsWithSongs();

@Transaction
@Query("SELECT * FROM Song")
public List<SongWithPlaylists> getSongsWithPlaylists();

İç içe ilişkileri tanımlama

Bazen, birbiriyle ilişkili üç veya daha fazla tablo grubunu sorgulamanız gerekebilir. Bu durumda, tablolar arasında iç içe geçmiş ilişkiler tanımlarsınız.

Müzik yayın uygulaması örneğinde, tüm kullanıcıları, her kullanıcının şarkı listelerinin tamamını ve her kullanıcı için her bir şarkı listesindeki şarkıların tamamını sorgulamak istediğinizi varsayalım. Kullanıcıların şarkı listeleriyle bire-çok ilişkisi, oynatma listelerinin de şarkılarla çoka-çok ilişkisi vardır. Aşağıdaki kod örneğinde bu varlıkları temsil eden sınıflar ve şarkı listeleri ile şarkılar arasındaki çoka-çok ilişkisine yönelik çapraz referans tablosu gösterilmektedir:

Kotlin

@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val userCreatorId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class User {
    @PrimaryKey public long userId;
    public String name;
    public int age;
}

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public long userCreatorId;
    public String playlistName;
}
@Entity
public class Song {
    @PrimaryKey public long songId;
    public String songName;
    public String artist;
}

@Entity(primaryKeys = {"playlistId", "songId"})
public class PlaylistSongCrossRef {
    public long playlistId;
    public long songId;
}

Öncelikle, bir veri sınıfı ve @Relation ek açıklamasını kullanarak kümenizdeki iki tablo arasındaki ilişkiyi normalde yaptığınız gibi modelleyin. Aşağıdaki örnekte, Playlist varlık sınıfı ile Song varlık sınıfı arasında çoka-çok ilişkiyi modelleyen bir PlaylistWithSongs sınıfı gösterilmektedir:

Kotlin

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = Junction(PlaylistSongCrossRef.class)
    )
    public List<Song> songs;
}

Bu ilişkiyi temsil eden bir veri sınıfı tanımladıktan sonra, kümenizdeki başka bir tablo ile ilk ilişki sınıfı arasındaki ilişkiyi modelleyen başka bir veri sınıfı oluşturun ve mevcut ilişkiyi yeni ilişki içinde "iç içe yerleştirin". Aşağıdaki örnekte, User varlık sınıfı ile PlaylistWithSongs ilişki sınıfı arasında bire-çok ilişkiyi modelleyen bir UserWithPlaylistsAndSongs sınıfı gösterilmektedir:

Kotlin

data class UserWithPlaylistsAndSongs(
    @Embedded val user: User
    @Relation(
        entity = Playlist::class,
        parentColumn = "userId",
        entityColumn = "userCreatorId"
    )
    val playlists: List<PlaylistWithSongs>
)

Java

public class UserWithPlaylistsAndSongs {
    @Embedded public User user;
    @Relation(
        entity = Playlist.class,
        parentColumn = "userId",
        entityColumn = "userCreatorId"
    )
    public List<PlaylistWithSongs> playlists;
}

UserWithPlaylistsAndSongs sınıfı, üçü varlık sınıfı arasındaki ilişkileri dolaylı olarak modeller: User, Playlist ve Song. Bu, Şekil 1'de gösterilmiştir.

UserWithOynatma ListeleriAndSongs, Kullanıcı ve Oynatma Listesiile Şarkılar arasındaki ilişkiyi modeller. Bu model de oynatma listesi ile şarkı arasındaki ilişkiyi modeller.

Şekil 1. Müzik yayın uygulaması örneğindeki ilişki sınıflarını gösteren şema.

Kümenizde başka tablo varsa kalan her tablo ile önceki tüm tablolar arasındaki ilişkiyi modelleyen ilişki sınıfı ile arasındaki ilişkiyi modellemek için bir sınıf oluşturun. Bu, sorgulamak istediğiniz tüm tablolar arasında bir iç içe ilişki zinciri oluşturur.

Son olarak, uygulamanızın ihtiyaç duyduğu sorgu işlevini açığa çıkarmak için DAO sınıfına bir yöntem ekleyin. Bu yöntem, Room'un birden fazla sorgu çalıştırmasını gerektirir. Bu nedenle, tüm işlemin ana makine olarak gerçekleştirilmesi için @Transaction ek açıklamasını ekleyin:

Kotlin

@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();

Ek kaynaklar

Odadaki varlıklar arasındaki ilişkileri tanımlama hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın.

Sana Özel

Videolar

Bloglar