การอ้างอิงข้อมูลที่ซับซ้อนโดยใช้ Room

ห้องพักมีฟังก์ชันสำหรับแปลงระหว่างประเภทพื้นฐานและแบบกล่อง แต่ไม่อนุญาตให้มีการอ้างอิงออบเจ็กต์ระหว่างเอนทิตี เอกสารนี้ อธิบายวิธีใช้ตัวแปลงประเภทและเหตุผลที่ห้องไม่รองรับวัตถุ บุคคล

ใช้ตัวแปลงประเภท

บางครั้งคุณอาจต้องการให้แอปจัดเก็บประเภทข้อมูลที่กำหนดเองไว้ในฐานข้อมูลเดียว คุณรองรับประเภทที่กำหนดเองได้โดยการระบุตัวแปลงประเภท ซึ่งเป็น ที่บอกห้องถึงวิธีแปลงประเภทที่กำหนดเองเป็นและจากประเภทที่รู้จัก ห้องแชทจะยังคงเดิม คุณสามารถระบุตัวแปลงประเภทโดยใช้ @TypeConverter

สมมติว่าคุณต้องการคงอินสแตนซ์ของ Date ไว้ใน ฐานข้อมูลห้องแชท ห้องแชทไม่ทราบวิธียืนยันออบเจ็กต์ Date รายการ คุณจึงต้องใช้ เพื่อกำหนดตัวแปลงประเภท ดังนี้

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

ตัวอย่างนี้กำหนดวิธีการสำหรับตัวแปลง 2 ประเภท ได้แก่ วิธีที่ใช้แปลง Date เป็นออบเจ็กต์ Long และออบเจ็กต์ที่แปลงค่าผกผันจาก Long ถึง Date เนื่องจาก Room รู้วิธียืนยันวัตถุ Long จึงใช้ ตัวแปลงเหล่านี้เพื่อคงออบเจ็กต์ Date รายการไว้

ต่อไป ให้เพิ่ม@TypeConverters เป็นคำอธิบายประกอบในคลาส AppDatabase เพื่อให้ห้องทราบเกี่ยวกับตัวแปลง ที่คุณกำหนด:

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

เมื่อกำหนดตัวแปลงประเภทเหล่านี้แล้ว คุณสามารถใช้ประเภทที่กำหนดเองได้ใน เอนทิตีและ DAO เหมือนกับที่คุณใช้ประเภทพื้นฐาน ดังนี้

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

ในตัวอย่างนี้ Room จะใช้ตัวแปลงประเภทที่กำหนดไว้ได้ทุกที่เนื่องจากคุณ ใส่คำอธิบายประกอบ AppDatabase ด้วย @TypeConverters อย่างไรก็ตาม คุณอาจประเภท ผู้แปลงเป็นเอนทิตีหรือ DAO ที่เฉพาะเจาะจงโดยใส่คำอธิบายประกอบใน @Entity หรือ @Dao ชั้นเรียนกับ @TypeConverters

การเริ่มต้นตัวแปลงประเภทการควบคุม

โดยปกติแล้ว Room จะจัดการการเริ่มต้นแปลงประเภทให้คุณ อย่างไรก็ตาม บางครั้งคุณอาจต้องส่งผ่านทรัพยากร Dependency เพิ่มเติมไปยังตัวแปลงประเภท ซึ่งหมายความว่าคุณต้องใช้แอปเพื่อควบคุมการเริ่มต้นโดยตรง ของเครื่องแปลงประเภทแล้ว ในกรณีดังกล่าว ให้ใส่คำอธิบายประกอบคลาสตัวแปลงด้วย @ProvidedTypeConverter:

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) {
    ...
  }
}

จากนั้น นอกเหนือจากการประกาศคลาสตัวแปลงใน @TypeConverters ให้ใช้ เวลา RoomDatabase.Builder.addTypeConverter() ในการส่งอินสแตนซ์ของคลาสตัวแปลงไปยัง RoomDatabase เครื่องมือสร้าง:

Kotlin

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

Java

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

ทำความเข้าใจสาเหตุที่ห้องแชทไม่อนุญาตการอ้างอิงวัตถุ

สิ่งสำคัญที่เรียนรู้: Room ไม่อนุญาตการอ้างอิงออบเจ็กต์ระหว่างคลาสเอนทิตี แต่คุณต้อง ขอข้อมูลที่แอปของคุณต้องการอย่างชัดเจน

การจับคู่ความสัมพันธ์จากฐานข้อมูลกับโมเดลออบเจ็กต์ที่เกี่ยวข้องนั้นเป็น ฝึกฝนและทำงานบนฝั่งเซิร์ฟเวอร์ได้ดี แม้ในขณะที่โปรแกรมโหลดอยู่ เมื่อมีการเข้าถึง เซิร์ฟเวอร์จะยังคงทำงานได้ดี

อย่างไรก็ตาม ในฝั่งไคลเอ็นต์ การโหลดแบบ Lazy Loading ประเภทนี้ทำไม่ได้เนื่องจาก ซึ่งมักจะเกิดขึ้นในเธรด UI และการค้นหาข้อมูลในดิสก์ใน UI ชุดข้อความก่อให้เกิดปัญหาด้านประสิทธิภาพอย่างมีนัยสำคัญ โดยปกติแล้ว เทรด UI จะมี ประมาณ 16 มิลลิวินาที ในการคำนวณและวาดเค้าโครงที่อัปเดตของกิจกรรม ดังนั้นแม้จะมี ใช้เวลาเพียง 5 มิลลิวินาที จึงยังเป็นไปได้ว่าแอปของคุณหมดเวลาเพื่อ วาดเฟรมที่ทำให้เกิดข้อบกพร่องทางสายตาที่สังเกตเห็นได้ การค้นหาอาจใช้เวลาแม้กระทั่ง มีเวลามากขึ้นในการดำเนินการให้เสร็จสมบูรณ์หากมีธุรกรรมแยกต่างหากที่ทำงานพร้อมกัน หรือ หากอุปกรณ์กำลังทำงานอื่นๆ ที่จำเป็นต้องใช้ดิสก์ ถ้าคุณไม่ได้ใช้โหมด Lazy แต่การโหลดแอปกลับดึงข้อมูลมากเกินความต้องการ จึงสร้างหน่วยความจำ ของ Google

การแมปวัตถุแบบสัมพันธ์กันมักจะให้การตัดสินใจนี้แก่นักพัฒนาซอฟต์แวร์เพื่อให้ สามารถทำทุกอย่างที่เหมาะกับกรณีการใช้งานของแอป นักพัฒนาซอฟต์แวร์มักจะ ตัดสินใจแชร์โมเดลระหว่างแอป กับ UI โซลูชันนี้ไม่ แต่ปรับขยายขนาดได้ดี เนื่องจากเมื่อ UI เปลี่ยนแปลงไปตามเวลา โมเดลที่แชร์ สร้างปัญหาที่นักพัฒนาซอฟต์แวร์คาดการณ์และแก้ไขข้อบกพร่องได้ยาก

ตัวอย่างเช่น ลองใช้ UI ที่โหลดรายการออบเจ็กต์ Book พร้อมหนังสือแต่ละเล่ม มีออบเจ็กต์ Author ในตอนแรกคุณอาจออกแบบการค้นหาให้ใช้ Lazy Loading กำลังโหลดเพื่อให้มีอินสแตนซ์ของ Book เรียกข้อมูลผู้แต่ง การดึงข้อมูลครั้งแรกของ ฟิลด์ author จะค้นหาฐานข้อมูล ในภายหลังคุณจึงตระหนักว่า ต้องแสดงชื่อผู้เขียนใน UI ของแอปด้วย คุณเข้าถึง ให้เข้าใจง่าย ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

Kotlin

authorNameTextView.text = book.author.name

Java

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

อย่างไรก็ตาม การเปลี่ยนแปลงที่ดูเหมือนจะไร้เดียงสานี้ทำให้มีการค้นหาตาราง Author ในเทรดหลัก

หากคุณค้นหาข้อมูลผู้แต่งล่วงหน้า การเปลี่ยนแปลงนั้นอาจทำได้ยาก วิธีโหลดข้อมูลหากคุณไม่ต้องการข้อมูลนั้นแล้ว ตัวอย่างเช่น หากแอปของคุณ UI ไม่จำเป็นต้องแสดงข้อมูล Author อีกต่อไป แอปของคุณโหลดได้อย่างมีประสิทธิภาพ ข้อมูลที่ไม่แสดงอีกต่อไป ทำให้สูญเสียพื้นที่หน่วยความจำอันมีค่า แอปของคุณ ประสิทธิภาพจะลดลงไปอีกหากคลาส Author อ้างอิงตารางอื่น เช่น Books

หากต้องการอ้างอิงหลายเอนทิตีพร้อมกันโดยใช้ห้องแชท คุณจะต้องสร้าง POJO ที่มีแต่ละเอนทิตี แล้วเขียนข้อความค้นหาที่เชื่อมเอนทิตีที่เกี่ยวข้อง โมเดลที่มีโครงสร้างดีนี้รวมกับการค้นหาที่มีประสิทธิภาพของห้อง ความสามารถในการตรวจสอบความถูกต้องช่วยให้แอปของคุณใช้ทรัพยากรน้อยลงเมื่อโหลด เพื่อปรับปรุงประสิทธิภาพของแอปและประสบการณ์ของผู้ใช้