Room'u kullanarak karmaşık verilere referans verme

Oda, temel ve kutulu türler arasında dönüştürme işlevi sunar ancak varlıklar arasında nesne referanslarına izin vermez. Bu belgede, tür dönüştürücülerin nasıl kullanılacağı ve Room'un nesne referanslarını neden desteklemediği açıklanmaktadır.

Tür dönüştürücüleri kullan

Bazen uygulamanızın özel bir veri türünü tek bir veritabanı sütununda depolaması gerekir. Özel türleri, tür dönüştürücüler sağlayarak desteklersiniz. Bu yöntemler, Room'a özel türlerin Oda'nın sürdürebileceği bilinen türlere nasıl dönüştürüleceğini bildiren yöntemlerdir. Tür dönüştürücüleri, @TypeConverter ek açıklamasını kullanarak tanımlarsınız.

Oda veritabanınızda Date örneklerini tutmanız gerektiğini varsayalım. Oda, Date nesnelerini nasıl koruyacağını bilemez. Bu nedenle, tür dönüştürücüleri tanımlamanız gerekir:

Kotlin

class Converters {
  @TypeConverter
  fun fromTimestamp(value: Long?): Date? {
    return value?.let { Date(it) }
  }

  @TypeConverter
  fun dateToTimestamp(date: Date?): Long? {
    return date?.time?.toLong()
  }
}

Java

public class Converters {
  @TypeConverter
  public static Date fromTimestamp(Long value) {
    return value == null ? null : new Date(value);
  }

  @TypeConverter
  public static Long dateToTimestamp(Date date) {
    return date == null ? null : date.getTime();
  }
}

Bu örnekte iki tür dönüştürücü yöntemi tanımlanmaktadır: bir Date nesnesini Long nesnesine dönüştüren bir yöntem ve Long nesnesinden Date nesnesine ters dönüştürme işlemi gerçekleştiren bir yöntem. Room, Long nesnelerini nasıl koruyacağını bildiğinden, Date nesnelerini saklamak için bu dönüştürücüleri kullanabilir.

Ardından, Oda'nın tanımladığınız dönüştürücü sınıfı hakkında bilgi sahibi olması için @TypeConverters ek açıklamasını AppDatabase sınıfına eklersiniz:

Kotlin

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

Java

@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
  public abstract UserDao userDao();
}

Bu tür dönüştürücüler tanımlandığında, özel türünüzü varlıklarınızda ve DAO'larınızda temel türleri kullandığınız gibi kullanabilirsiniz:

Kotlin

@Entity
data class User(private val birthday: Date?)

@Dao
interface UserDao {
  @Query("SELECT * FROM user WHERE birthday = :targetDate")
  fun findUsersBornOnDate(targetDate: Date): List<User>
}

Java

@Entity
public class User {
  private Date birthday;
}

@Dao
public interface UserDao {
  @Query("SELECT * FROM user WHERE birthday = :targetDate")
  List<User> findUsersBornOnDate(Date targetDate);
}

Bu örnekte, AppDatabase öğesine @TypeConverters ek açıklaması eklediğiniz için Room tanımlı tür dönüştürücüyü her yerde kullanabilir. Ancak @Entity veya @Dao sınıflarınıza @TypeConverters ile ek açıklama ekleyerek tür dönüştürücüleri de belirli varlıklara veya DAO'lara dahil edebilirsiniz.

Kontrol türü dönüştürücü başlatma

Normalde Room, sizin için tür dönüştürücülerin örneklendirmesini işler. Ancak, bazen tür dönüştürücü sınıflarınıza ek bağımlılıklar aktarmanız gerekebilir. Bu da uygulamanızın, tür dönüştürücülerinizin başlatılmasını doğrudan kontrol etmesi gerektiği anlamına gelir. Bu durumda, dönüştürücü sınıfınıza @ProvidedTypeConverter ek açıklaması ekleyin:

Kotlin

@ProvidedTypeConverter
class ExampleConverter {
  @TypeConverter
  fun StringToExample(string: String?): ExampleType? {
    ...
  }

  @TypeConverter
  fun ExampleToString(example: ExampleType?): String? {
    ...
  }
}

Java

@ProvidedTypeConverter
public class ExampleConverter {
  @TypeConverter
  public Example StringToExample(String string) {
    ...
  }

  @TypeConverter
  public String ExampleToString(Example example) {
    ...
  }
}

Ardından, dönüştürücü sınıfınızı @TypeConverters içinde belirtmenin yanı sıra RoomDatabase.Builder.addTypeConverter() yöntemini kullanarak dönüştürücü sınıfınızın bir örneğini RoomDatabase oluşturucuya geçirin:

Kotlin

val db = Room.databaseBuilder(...)
  .addTypeConverter(exampleConverterInstance)
  .build()

Java

AppDatabase db = Room.databaseBuilder(...)
  .addTypeConverter(exampleConverterInstance)
  .build();

Room'un nesne referanslarına neden izin vermediğini anlama

Temel çıkarım: Oda, varlık sınıfları arasında nesne referanslarına izin vermez. Bunun yerine, uygulamanızın ihtiyaç duyduğu verileri açıkça istemelisiniz.

Bir veritabanından ilgili nesne modeliyle ilişkilerin eşlenmesi yaygın bir uygulamadır ve sunucu tarafında çok iyi sonuç verir. Program, alanları kendilerine erişildiğinde yüklese bile sunucu yine de iyi performans gösterir.

Bununla birlikte, istemci tarafında bu tür geç yükleme, genellikle kullanıcı arayüzü iş parçacığında gerçekleştiği ve kullanıcı arayüzü iş parçacığındaki diskteki bilgilerin sorgulanması önemli performans sorunları oluşturduğu için uygun değildir. UI iş parçacığının, bir etkinliğin güncellenmiş düzenini hesaplamak ve çizmek için yaklaşık 16 ms'si vardır. Bu nedenle, bir sorgu yalnızca 5 ms Paralel olarak çalışan ayrı bir işlem varsa veya cihaz diski yoğun şekilde kullanan başka görevler çalıştırıyorsa sorgunun tamamlanması daha da uzun sürebilir. Ancak geç yükleme kullanmazsanız uygulamanız gerekenden daha fazla veri getirir ve bu da bellek tüketimi sorunlarına yol açar.

Nesne ilişki eşlemelerinde, uygulamalarının kullanım alanlarına en uygun olanı yapabilmeleri için genellikle bu kararı geliştiricilere bırakırlar. Geliştiriciler genellikle modeli uygulamaları ve kullanıcı arayüzü arasında paylaşmaya karar verirler. Ancak bu çözüm iyi ölçeklenemez. Ancak kullanıcı arayüzü zamanla değiştikçe paylaşılan model geliştiricilerin tahmin etmesi ve hata ayıklaması zor sorunlar yaratır.

Örneğin, her kitap bir Author nesnesine sahip olacak şekilde Book nesnelerinin listesini yükleyen bir kullanıcı arayüzünü düşünün. Başlangıçta sorgularınızı Book örneklerinin yazarı almasını sağlamak için geç yükleme özelliğini kullanacak şekilde tasarlayabilirsiniz. author alanı ilk kez alındığında veritabanı sorgulanır. Bir süre sonra, uygulamanızın kullanıcı arayüzünde yazar adını da görüntülemeniz gerektiğini fark edersiniz. Aşağıdaki kod snippet'inde gösterildiği gibi bu ada yeterince kolay erişebilirsiniz:

Kotlin

authorNameTextView.text = book.author.name

Java

authorNameTextView.setText(book.getAuthor().getName());

Ancak masum görünen bu değişiklik, Author tablosunun ana iş parçacığında sorgulanmasına neden olur.

Yazar bilgilerini önceden sorguladığınızda, artık ihtiyaç duymadığınız verilerin yüklenme biçimini değiştirmek zorlaşır. Örneğin, uygulamanızın kullanıcı arayüzünün Author bilgilerini artık görüntülemesi gerekmiyorsa uygulamanız artık göstermediği verileri etkili bir şekilde yükleyerek değerli bellek alanı boşa harcar. Author sınıfı Books gibi başka bir tabloya referans veriyorsa uygulamanızın verimliliği daha da düşer.

Oda özelliğini kullanarak aynı anda birden çok varlığa referans vermek için bunun yerine her varlığı içeren bir POJO oluşturur ve ilgili tabloları birleştiren bir sorgu yazarsınız. Bu iyi yapılandırılmış model, Room'un güçlü sorgu doğrulama özellikleriyle bir araya geldiğinde uygulamanızın veri yüklerken daha az kaynak tüketmesini sağlayarak uygulamanızın performansını ve kullanıcı deneyimini iyileştirir.