หากคุณเคยใช้งานข้อมูลที่มีอยู่บนแอปพลิเคชัน Android คุณอาจคุ้นเคยกับไลบรารี ORM (Object-Relational Mapping)

ORM ช่วยให้เขียนคำสั่ง SQL โดยใช้กระบวนทัศน์เชิงวัตถุได้ง่ายขึ้น แทนที่จะใช้ภาษา SQL โดยตรง Mario Hoyos เขียนบทความดีๆ เกี่ยวกับ ORM ซึ่งคุณสามารถอ่านได้ "ที่นี่" อ่านเพิ่มเติมเกี่ยวกับ ORM ได้ที่ "ที่นี่"

ใน Android ORM ที่พบบ่อยที่สุดคือ Realm และ Room ฉันไม่เคยร่วมงานกับ Realm มาก่อน แต่ฉันคิดว่าแนวคิดนี้เกือบจะเหมือนกันเกี่ยวกับวิธีการใช้งาน

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

ในบทความนี้ ฉันจะอธิบายเกี่ยวกับการย้ายข้อมูลแบบง่ายๆ โดยใช้ Kotlin

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

@Database(
    entities = [
        Users::class
    ],
        version = 1,
        exportSchema = false)
@TypeConverters( )
abstract class AppDatabase : RoomDatabase(){
abstract fun usersDao(): UsersDao 
   
companion object {
        @Volatile private var instance: AppDatabase?=null
        private val LOCK = Any()
        operator fun invoke(context: Context) = instance ?: synchronized(LOCK){
            instance ?: buildDatabase(context).also { instance = it}
        }
        
private fun buildDatabase(context: Context) = androidx.room.Room.databaseBuilder(context,
            AppDatabase::class.java, "users.database")
            .build()
    }
}

โค้ดด้านบนแสดงวิธีการตั้งค่าอินสแตนซ์เดียวของฐานข้อมูลของคุณ ส่วนหนึ่งของการกำหนดค่าเกี่ยวข้องกับการตั้งค่าคลาสนามธรรมสำหรับ DAO (Data Access Objects) ซึ่งฉันจะไม่เข้าไปด้วย

ต่อไปเราจะสร้างตารางผู้ใช้ นี่คือตารางที่เราจะทำการสลับกัน

@Entity(tableName = "users_table")
data class Users(
@PrimaryKey(autoGenerate = true)
   var id: Int,
var name: String?,
var email: String?
)

วิธีการตั้งค่าตารางผู้ใช้ของเรานั้นมีคีย์หลักที่สร้างขึ้นโดยอัตโนมัติ ชื่อประเภท String และที่อยู่อีเมลประเภท String เช่นกัน ฉันจะไม่เขียน DAO สำหรับตารางนี้

สมมติว่าตอนนี้เราได้เปลี่ยนคอลัมน์ “name” เป็น “user_name” ดังนี้:

...//
var user_name: String?,
..//

หากรันแอปพลิเคชันของเราหลังจากนั้น เราจะได้รับข้อผิดพลาดด้านล่าง:

Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. 
You can simply fix this by increasing the version number.

ตามที่รายงานข้อผิดพลาดกล่าวไว้ สิ่งแรกที่เราต้องทำคืออัปเกรดหมายเลขเวอร์ชันฐานข้อมูล:

@Database(
    entities = [
        Users::class
    ],
        version = 2, // change this here to a number higher than 1
        exportSchema = false)

การทำเช่นนี้จะยังส่งผลให้เกิดข้อผิดพลาดโดยที่ Room ไม่สามารถสร้างความถูกต้องของข้อมูลได้ เนื่องจากสคีมายังคงแตกต่างออกไปและไม่มีการโยกย้าย

แก้ไขด่วนโดยใช้การโยกย้ายแบบทำลายล้าง

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

...//
private fun buildDatabase(context: Context) = androidx.room.Room.databaseBuilder(context,
            AppDatabase::class.java, "users.database")
            .fallbackToDestructiveMigration() // destructive migration
            .build()
    }
...//

การดำเนินการย้ายข้อมูลเพื่อบันทึกข้อมูลก่อนหน้า

หากคุณไม่ต้องการให้ผู้ใช้สูญเสียข้อมูลเมื่อมีการอัปเดต ตัวเลือกที่ดีที่สุดคือการย้ายข้อมูลให้ถูกต้อง ฉันรู้ การอพยพ...

เมื่อฉันได้ยินเรื่องนี้ครั้งแรก ฉันคิดว่ามันจะเป็นเรื่องยาก แต่ความจริงก็คือ Room ทำให้การโยกย้ายเหล่านี้ราบรื่นและง่ายดาย

ลองใช้ตัวอย่างก่อนหน้าของการเปลี่ยนชื่อคอลัมน์จาก “name” เป็น “user_name” ในตาราง Users กัน

การโยกย้ายจะมีลักษณะดังนี้:

...//
// migration to alter the user_name in Users table
private val MIGRATION
Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. 
You can simply fix this by increasing the version number.
2 = object : Migration(1,2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE users_table RENAME name TO user_name") } } ...//

หลังจากนี้ สิ่งถัดไปที่คุณต้องทำคือเพิ่มการโยกย้ายไปยังฐานข้อมูลของคุณ

บางสิ่งที่ควรทราบ:
1. ชื่อ MIGRATION

Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. 
You can simply fix this by increasing the version number.
2 สามารถเป็นอะไรก็ได้ แต่เป็นเรื่องปกติที่จะตั้งชื่อและผนวกหมายเลขเวอร์ชันก่อนหน้าและหมายเลขถัดไปสำหรับฐานข้อมูล
2 อย่างไรก็ตาม Migration(1,2) จะต้องตรงกับหมายเลขเวอร์ชันก่อนหน้าและถัดไปสำหรับฐานข้อมูลของคุณ

เอาล่ะ ตอนนี้เรามาเพิ่มการโยกย้ายไปยังฐานข้อมูลของเรากันดีกว่า:

...//
private fun buildDatabase(context: Context) = androidx.room.Room.databaseBuilder(context,
    AppDatabase::class.java, "users.database")
    .addMigrations(MIGRATION
Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. 
You can simply fix this by increasing the version number.
2) // ADD MIGRATION .build() ...//

หากคุณมีการย้ายข้อมูลมากกว่าหนึ่งรายการ คุณจะมีเครื่องหมายจุลภาคและเพิ่มต่อไปในฟังก์ชัน addMigrations()

แค่นั้นแหละ. ง่ายก็ทำ

อ่านเพิ่มเติมเกี่ยวกับการโยกย้ายได้จากแหล่งข้อมูลด้านล่าง:

  1. https://developer.android.com/reference/android/arch/persistence/room/RoomDatabase.Builder
  2. https://developer.android.com/training/data-storage/room/migrating-db-versions