การย้ายฐานข้อมูลห้องหากมีการเพิ่มตารางใหม่เท่านั้น


107

อย่าถือว่าฉันมีฐานข้อมูลห้องแบบธรรมดา:

@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

ตอนนี้ฉันกำลังเพิ่มเอนทิตีใหม่: Petและเวอร์ชันที่ถูกกระแทกเป็น 2:

@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

แน่นอนว่า Room มีข้อยกเว้น: java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

สมมติว่าฉันไม่ได้เปลี่ยนUserคลาส (ดังนั้นข้อมูลทั้งหมดจึงปลอดภัย) ฉันต้องจัดเตรียมการโยกย้ายซึ่งเพิ่งสร้างตารางใหม่ ดังนั้นฉันกำลังมองหาชั้นเรียนที่สร้างโดยห้องค้นหาแบบสอบถามที่สร้างขึ้นเพื่อสร้างตารางใหม่ของฉันคัดลอกและวางลงในการย้ายข้อมูล:

final Migration MIGRATION_1_2 =
        new Migration(1, 2) {
            @Override
            public void migrate(@NonNull final SupportSQLiteDatabase database) {
                database.execSQL("CREATE TABLE IF NOT EXISTS `Pet` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))");
            }
        };

อย่างไรก็ตามฉันคิดว่ามันไม่สะดวกที่จะทำด้วยตนเอง มีวิธีบอกห้องไหม : ฉันไม่ได้แตะตารางใด ๆ ที่มีอยู่ข้อมูลจึงปลอดภัย โปรดสร้างการย้ายข้อมูลให้ฉัน?


คุณพบวิธีแก้ปัญหานี้หรือไม่?
Mikkel Larsen

3
ฉันมีปัญหาเดียวกันและได้รับการแก้ไขเช่นเดียวกับที่คุณทำและไม่พบวิธีแก้ปัญหา ดีใจที่ฉันไม่ได้อยู่คนเดียวแล้ว :)
Mikkel Larsen

3
เหมือนกันที่นี่ ฉันคิดว่ามันไม่สะดวกมากที่ห้องนั้นสามารถสร้าง
คิวรี create

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

3
ฉันไม่แน่ใจว่าจะมีประโยชน์หรือไม่ แต่ Room มีตัวเลือกในการส่งออกสคีมาฐานข้อมูลเป็นไฟล์ JSON developer.android.com/training/data-storage/room/…เห็นได้ชัดว่านี่ยังคงหมายถึงการเพิ่มสคริปต์การย้ายข้อมูลด้วยตนเอง แต่คุณไม่จำเป็นต้องกำหนดเส้นทางผ่านคลาสที่สร้างขึ้นอัตโนมัติเพื่อรับคำสั่ง SQL ของคุณ
James Lendrem

คำตอบ:


84

ห้องพักไม่ไม่ได้2.1.0-alpha03มีดีระบบการโยกย้ายอย่างน้อยไม่ได้จนกว่า

ดังนั้นจนกว่าเราจะมีระบบการย้ายข้อมูลที่ดีขึ้นมีวิธีแก้ปัญหาบางประการเพื่อให้การย้ายข้อมูลในห้องทำได้ง่าย

เนื่องจากไม่มีวิธีการเช่น@Database(createNewTables = true) หรือMigrationSystem.createTable(User::class)ซึ่งควรมีอย่างใดอย่างหนึ่งวิธีเดียวที่เป็นไปได้คือการทำงาน

CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))

ภายในmigrateวิธีการของคุณ

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))")
    }
}

เพื่อให้เหนือสคริปต์SQLคุณมี 4 วิธี

1. เขียนด้วยตัวเอง

โดยทั่วไปคุณต้องเขียนสคริปต์ด้านบนที่จะตรงกับสคริปต์ที่สร้างห้อง วิธีนี้เป็นไปได้ไม่เป็นไปได้ (พิจารณาว่าคุณมี 50 ช่อง)

2. ส่งออกสคีมา

หากคุณรวมexportSchema = trueไว้ใน@Databaseคำอธิบายประกอบของคุณห้องจะสร้างสคีมาฐานข้อมูลภายใน / สกีมาของโฟลเดอร์โครงการของคุณ ลักษณะการใช้งานคือ

@Database(entities = [User::class], version = 2, exportSchema = true)
abstract class AppDatabase : RoomDatabase {
   //...
}

ตรวจสอบให้แน่ใจว่าคุณได้รวมบรรทัดด้านล่างในbuild.gradeโมดูลแอพของคุณแล้ว

kapt {
    arguments {
        arg("room.schemaLocation", "$projectDir/schemas".toString())
    }
} 

เมื่อคุณเรียกใช้หรือสร้างโปรเจ็กต์คุณจะได้รับไฟล์ JSON 2.jsonซึ่งมีคำค้นหาทั้งหมดในฐานข้อมูล Room ของคุณ

  "formatVersion": 1,
  "database": {
    "version": 2,
    "identityHash": "325bd539353db508c5248423a1c88c03",
    "entities": [
      {
        "tableName": "User",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": true
          },

ดังนั้นคุณสามารถรวมวิธีการข้างต้นไว้createSqlในmigrateวิธีการของคุณ

3. รับแบบสอบถามจาก AppDatabase_Impl

หากคุณไม่ต้องการส่งออกสคีมาคุณยังสามารถรับแบบสอบถามได้โดยการเรียกใช้หรือสร้างโครงการซึ่งจะสร้างAppDatabase_Impl.javaไฟล์ และภายในไฟล์ที่ระบุคุณสามารถมีได้

@Override
public void createAllTables(SupportSQLiteDatabase _db) {
  _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");

ภายในcreateAllTablesเมธอดจะมีสคริปต์สร้างของเอนทิตีทั้งหมด คุณสามารถรับได้และรวมไว้ในmigrateวิธีการของคุณ

4. การประมวลผลคำอธิบายประกอบ

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

kapt "androidx.room:room-compiler:$room_version"

นั่นหมายความว่าคุณสามารถทำเช่นเดียวกันและสร้างไลบรารีการประมวลผลคำอธิบายประกอบของคุณเองซึ่งจะสร้างการสืบค้นสร้างที่จำเป็นทั้งหมดสำหรับคุณ

ความคิดที่จะทำให้ห้องสมุดการประมวลผลคำอธิบายประกอบคำอธิบายประกอบห้องพักของและ@Entity @Databaseใช้ชั้นเรียนที่มีคำอธิบายประกอบ@Entityเช่น นี่คือขั้นตอนที่คุณจะต้องปฏิบัติตาม

  1. สร้างใหม่StringBuilderและต่อท้าย "CREATE TABLE IF NOT EXISTS"
  2. ได้รับชื่อของตารางทั้งจากclass.simplenameหรือโดยการด้านการtableName @Entityเพิ่มลงในไฟล์StringBuilder
  3. จากนั้นสำหรับแต่ละฟิลด์ในชั้นเรียนของคุณให้สร้างคอลัมน์ของ SQL ใช้ชื่อประเภทความว่างเปล่าของฟิลด์โดยตัวฟิลด์เองหรือตาม@ColumnInfoคำอธิบายประกอบ สำหรับทุกฟิลด์คุณต้องเพิ่มid INTEGER NOT NULLสไตล์ของคอลัมน์ให้กับStringBuilderไฟล์.
  4. เพิ่มคีย์หลักโดย @PrimaryKey
  5. เพิ่มForeignKeyและIndicesถ้ามี
  6. หลังจากเสร็จสิ้นการแปลงเป็นสตริงและบันทึกในคลาสใหม่ที่คุณต้องการใช้ ตัวอย่างเช่นบันทึกไว้ด้านล่าง
public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

จากนั้นคุณสามารถใช้เป็นไฟล์

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL(UserSqlUtils().createTable)
    }
}

ฉันสร้างห้องสมุดดังกล่าวสำหรับตัวเองซึ่งคุณสามารถเช็คเอาต์และใช้ในโครงการของคุณได้ด้วย โปรดทราบว่าไลบรารีที่ฉันสร้างขึ้นไม่เต็มและเป็นไปตามข้อกำหนดของฉันสำหรับการสร้างตาราง

RoomExtension เพื่อการโยกย้ายที่ดีขึ้น

แอปพลิเคชันที่ใช้ RoomExtension

หวังว่าจะเป็นประโยชน์

อัปเดต

เมื่อเขียนคำตอบนี้เวอร์ชันห้องคือ2.1.0-alpha03และเมื่อฉันส่งอีเมลถึงนักพัฒนาซอฟต์แวร์ฉันได้รับคำตอบ

คาดว่าจะมีระบบการโยกย้ายที่ดีขึ้นใน 2.2.0

น่าเสียดายที่เรายังขาดระบบการย้ายข้อมูลที่ดีกว่า


3
คุณช่วยชี้จุดที่คุณอ่านว่าห้อง 2.2.x จะมีการโยกย้ายที่ดีกว่าได้ไหม ฉันไม่พบสิ่งใดที่อ้างสิทธิ์นั้นและเนื่องจากเรากำลังดำเนินการกับ 2.1.0 เบต้าอยู่ในขณะนี้สิ่งที่อยู่ใน 2.2.0 ดูเหมือนจะไม่เป็นที่รู้จัก
jkane001

1
@ jkane001 ฉันส่งอีเมลถึงผู้พัฒนาห้องพักคนหนึ่งและได้รับคำตอบ ไม่มีประกาศสาธารณะดังกล่าวเกี่ยวกับ 2.2.x (หรือยัง?)
musooff

5
ขณะนี้ใช้เวอร์ชัน 2.2.2 และยังไม่มีการโยกย้ายที่ดีขึ้น :( อย่างไรก็ตามนี่เป็นคำตอบที่ยอดเยี่ยมและช่วยให้ฉันประหยัดงานได้มากจึง +1 สำหรับสิ่งนั้น
smitty1

@androiddeveloper ทั้งหมดยกเว้น # 4 ซึ่งเป็นการประมวลผลคำอธิบายประกอบ
musooff

1
@musooff ฉันคิดว่ามันโอเคที่จะเพิ่มการสร้างตาราง เป็นวิธีที่ปลอดภัยที่สุดในการคัดลอกโค้ดจากฟังก์ชัน "createAllTables"
นักพัฒนา Android

5

ขออภัย Room ไม่รองรับการสร้างตารางอัตโนมัติโดยที่ข้อมูลไม่สูญหาย

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


0

คุณสามารถทำได้ด้วยวิธีนี้ -

@Database(entities = {User.class, Pet.class}, version = 2)

abstract class AppDatabase extends RoomDatabase {
public abstract Dao getDao();
public abstract Dao getPetDao();
}

ส่วนที่เหลือจะเหมือนกับที่คุณได้กล่าวไว้ข้างต้น -

 db = Room.databaseBuilder(this, AppDatabase::class.java, "your_db")
        .addMigrations(MIGRATION_1_2).build()

อ้างอิง - สำหรับข้อมูลเพิ่มเติม


0

คุณสามารถเพิ่มคำสั่ง gradle ต่อไปนี้ใน defaultConfig ของคุณใน app.gradle ของคุณ:

javaCompileOptions {
        annotationProcessorOptions {
            arguments = ["room.schemaLocation":
                                 "$projectDir/schemas".toString()]
        }
    }

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

ตัวอย่างเช่นนี่มาจากสคีมาที่ฉันสร้างขึ้น:

"tableName": "assets",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` INTEGER NOT NULL, `type` INTEGER NOT NULL, `base` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`asset_id`))"

ดังนั้นฉันจึงคัดลอกและวางคำสั่ง createSql และเปลี่ยน "$ {TABLE_NAME}" เป็น "assets" ชื่อตารางและคำสั่งสร้างห้องที่สร้างขึ้นโดยอัตโนมัติ voila


-1

ในกรณีนี้คุณไม่จำเป็นต้องทำการโยกย้ายคุณสามารถเรียก .fallbackToDestructiveMigration () เมื่อคุณสร้างอินสแตนซ์ฐานข้อมูล

ตัวอย่าง:

    instance = Room.databaseBuilder(context, AppDatabase.class, "database name").fallbackToDestructiveMigration().build();

และอย่าลืมเปลี่ยนเวอร์ชันของฐานข้อมูล


โซลูชันนี้จะลบข้อมูลทั้งหมดของฉันออกจากตารางที่มีอยู่
Piotr Aleksander Chmielowski

น่าเสียดายที่ใช่ "คุณสามารถเรียกวิธีนี้เพื่อเปลี่ยนลักษณะการทำงานนี้เพื่อสร้างฐานข้อมูลใหม่แทนที่จะหยุดทำงานโปรดทราบว่าการดำเนินการนี้จะลบข้อมูลทั้งหมดในตารางฐานข้อมูลที่จัดการโดย Room"
rudicjovan

-2

บางทีในกรณีนี้ (ถ้าคุณสร้างตารางใหม่โดยไม่เปลี่ยนตารางอื่น) คุณสามารถทำได้โดยไม่ต้องสร้างการโยกย้ายใด ๆ เลย?


1
ไม่ในกรณีนี้ห้องนี้มีบันทึก: java.lang.IllegalStateException: จำเป็นต้องย้ายจาก {old_version} เป็น {new_version}
Max Makeichik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.