यदि केवल नई तालिका जोड़ी जाती है तो कक्ष डेटाबेस माइग्रेशन


98

मान लें, मेरे पास एक साधारण कक्ष डेटाबेस है:

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

बेशक, कमरा एक अपवाद फेंकता है: 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`))");
            }
        };

हालाँकि मुझे इसे मैन्युअल रूप से करना असुविधाजनक लगता है। क्या कमरा बताने का कोई तरीका है: मैं किसी मौजूदा तालिका को नहीं छू रहा हूं, इसलिए डेटा सुरक्षित है। कृपया मेरे लिए माइग्रेशन बनाएं?


क्या आपको इसका हल मिला?
मिकेल लार्सन

3
मेरे पास एक ही मुद्दा था और इसे उसी तरह से तय किया जैसे आपने किया था, और न तो कोई समाधान मिला है। खुशी है कि मैं अकेला नहीं हूँ। :)
मिककेल लार्सन

3
मुझे भी। मुझे यह बहुत असुविधाजनक लगता है कि कमरा database_impl के अंदर क्रिएट क्वेरी उत्पन्न करने में सक्षम है, लेकिन माइग्रेशन शुरू होते ही, बस टेबल नहीं बना सकते हैं ....
JacksOnF1re

1
मैं इस तरह की सुविधा के लिए बहुत कुछ दूंगा ... माइग्रेशन और फालबैक मैकेनिज्म को मिलाना भी अच्छा
रहेगा

3
मुझे यकीन नहीं है कि यह उपयोगी होगा, लेकिन कमरे में डेटाबेस स्कीमा को JSON फ़ाइल में निर्यात करने का विकल्प होता है। developer.android.com/training/data-storage/room/… जाहिर है इसका मतलब अभी भी मैन्युअल रूप से माइग्रेशन स्क्रिप्ट जोड़ना होगा, लेकिन आपको अपने एसक्यूएल स्टेटमेंट को प्राप्त करने के लिए ऑटो-जेनरेट की गई कक्षाओं के माध्यम से रूट करने की आवश्यकता नहीं होगी।
जेम्स लैन्ड्रेम

जवाबों:


75

कक्ष करता नहीं कम से कम जब तक नहीं एक अच्छा प्रवासन प्रणाली है, 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, जिसमें आपके कक्ष डेटाबेस के भीतर सभी प्रश्न हैं।

  "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. एनोटेशन प्रोसेसिंग।

जैसा कि आप अनुमान लगा सकते हैं, कक्ष उपर्युक्त सभी को उत्पन्न करता है schema, और AppDatabase_Implसंकलन समय के भीतर और एनोटेशन प्रोसेसिंग के साथ फाइलें जो आप के साथ जोड़ते हैं

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

इसका मतलब है कि आप भी ऐसा ही कर सकते हैं और अपनी खुद की एनोटेशन प्रोसेसिंग लाइब्रेरी बना सकते हैं जो आपके लिए सभी आवश्यक प्रश्न तैयार करती है।

यह विचार करना है कि कमरे के एनोटेशन @Entityऔर के लिए एनोटेशन प्रोसेसिंग लाइब्रेरी बनाना है @Database@Entityउदाहरण के लिए एनोटेट किया गया एक वर्ग लें । ये वो स्टेप्स हैं जिनका आपको पालन करना होगा

  1. एक नया StringBuilderऔर अपीयर बनाएं "यदि आवश्यक नहीं है तो बनाएँ"
  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

4
वर्तमान में 2.2.2 संस्करण पर है और अभी भी कोई बेहतर प्रवासन नहीं है :( हालांकि, यह एक उत्कृष्ट उत्तर है और मुझे इसके लिए काम का एक टन बच गया है।
smitty1


1
@ मूसोफ़ वास्तव में मुझे लगता है कि तालिका निर्माण को जोड़ना ठीक है। यह "createAllTables" फ़ंक्शन से कोड को कॉपी करने का सबसे सुरक्षित तरीका है।
एंड्रॉइड डेवलपर 11

3

क्षमा करें, कक्ष डेटा हानि के बिना तालिकाओं के ऑटो-निर्माण का समर्थन नहीं करता है।

माइग्रेशन लिखना अनिवार्य है। अन्यथा, यह सभी डेटा मिटा देगा और नई तालिका संरचना तैयार करेगा।


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

आप अपने ऐप्स में अपने डिफॉल्टकॉन्फिग में निम्न ग्रेड कमांड जोड़ सकते हैं।

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

जब आप इसे चलाते हैं तो यह तालिका के नामों की सूची को उनके प्रासंगिक सृजन विवरणों के साथ संकलित करेगा, जिनसे आप अपनी माइग्रेशन ऑब्जेक्ट्स में कॉपी और पेस्ट कर सकते हैं। आपको तालिका के नाम बदलने पड़ सकते हैं।

उदाहरण के लिए यह मेरे उत्पन्न स्कीमा से है:

"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}' को 'एसेट्स' टेबल के नाम से बदल देता हूं, और वॉयला ऑटो जेनरेटेड रूम क्रिएट करता है।


-1

इस स्थिति में, आपको माइग्रेशन करने की आवश्यकता नहीं है, जब आप डेटाबेस उदाहरण बना रहे हैं, तो आप .fallbackToDestructiveMigration () कॉल कर सकते हैं।

उदाहरण:

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

और डेटाबेस संस्करण को बदलने के लिए मत भूलना।


यह समाधान मेरे सभी डेटा को मौजूदा तालिकाओं से हटा देगा।
पिओटर अलेक्जेंडर च्मिलोव्स्की

दुर्भाग्य से हाँ। "आप क्रैश होने के बजाय डेटाबेस को फिर से बनाने के लिए इस व्यवहार को बदलने के लिए इस पद्धति को कॉल कर सकते हैं। ध्यान दें कि यह कक्ष द्वारा प्रबंधित डेटाबेस तालिकाओं में सभी डेटा को हटा देगा।"
रदिजकोवन

-2

हो सकता है कि इस मामले में (यदि आपने दूसरों को बदले बिना केवल नई तालिका बनाई है) तो क्या आप ऐसा कर सकते हैं जिससे कोई भी माइग्रेशन उत्पन्न न हो?


1
नहीं, इस मामले में कमरे लॉग में फेंकता है: java.lang.IllegalStateException: {old_version} से {new_version} तक का माइग्रेशन आवश्यक है
Max Makeichik
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.