Room veritabanınızı taşıyın

Uygulamanızda özellik ekleyip değiştirirken Oda varlığınızı da değiştirmeniz gerekir temel veri tabanı tablolarını bu değişiklikleri yansıtacak şekilde değiştirmeniz gerekir. Bu önemli veri tabanında bulunan kullanıcı verilerini korumak için bir uygulama güncelleme, veritabanı şemasını değiştirir.

Oda, artımlı taşıma için hem otomatik hem de manuel seçenekleri destekler. Otomatik taşıma işlemleri çoğu temel şema değişikliği için kullanılabilir ancak Daha karmaşık değişiklikler için taşıma yollarını manuel olarak tanımlayabilirsiniz.

Otomatik taşıma işlemleri

İki veritabanı sürümü arasında otomatik taşıma bildirmek için @AutoMigration ek açıklaması autoMigrations @Database konumundaki mülk:

Kotlin

// Database class before the version update.
@Database(
  version = 1,
  entities = [User::class]
)
abstract class AppDatabase : RoomDatabase() {
  ...
}

// Database class after the version update.
@Database(
  version = 2,
  entities = [User::class],
  autoMigrations = [
    AutoMigration (from = 1, to = 2)
  ]
)
abstract class AppDatabase : RoomDatabase() {
  ...
}

Java

// Database class before the version update.
@Database(
  version = 1,
  entities = {User.class}
)
public abstract class AppDatabase extends RoomDatabase {
  ...
}

// Database class after the version update.
@Database(
  version = 2,
  entities = {User.class},
  autoMigrations = {
    @AutoMigration (from = 1, to = 2)
  }
)
public abstract class AppDatabase extends RoomDatabase {
  ...
}

Otomatik taşıma özellikleri

Oda, belirsiz şema değişiklikleri algılar ve yeni bir taşıma planına izin verdiğinizde, derleme zamanı hatası vererek bir AutoMigrationSpec. Bu durum genellikle taşıma işlemi aşağıdakilerden birini içerdiğinde gerçekleşir:

  • Tablo silme veya yeniden adlandırma.
  • Sütun silme veya yeniden adlandırma

Odaya eklemek istediğiniz ek bilgileri AutoMigrationSpec taşıma yollarının doğru şekilde oluşturulması gerekir. Şu özelliklere sahip statik bir sınıf tanımlayın: RoomDatabase sınıfınıza AutoMigrationSpec uygular ve buna ek açıklama ekler aşağıdakilerden biri veya daha fazlası:

AutoMigrationSpec uygulamasını otomatik taşıma işleminde kullanmak için ilgili @AutoMigration ek açıklamasındaki spec özelliği:

Kotlin

@Database(
  version = 2,
  entities = [User::class],
  autoMigrations = [
    AutoMigration (
      from = 1,
      to = 2,
      spec = AppDatabase.MyAutoMigration::class
    )
  ]
)
abstract class AppDatabase : RoomDatabase() {
  @RenameTable(fromTableName = "User", toTableName = "AppUser")
  class MyAutoMigration : AutoMigrationSpec
  ...
}

Java

@Database(
  version = 2,
  entities = {AppUser.class},
  autoMigrations = {
    @AutoMigration (
      from = 1,
      to = 2,
      spec = AppDatabase.MyAutoMigration.class
    )
  }
)
public abstract class AppDatabase extends RoomDatabase {
  @RenameTable(fromTableName = "User", toTableName = "AppUser")
  static class MyAutoMigration implements AutoMigrationSpec { }
  ...
}

Otomatik taşıma işlemi tamamlandıktan sonra uygulamanızın daha fazla işlem yapması gerekiyorsa kullanabileceğimiz onPostMigrate(). Bu yöntemi AutoMigrationSpec içinde uygularsanız Oda, şunu sonrasında çağırır: otomatik taşıma tamamlanır.

Manuel taşıma işlemleri

Taşıma işleminin karmaşık şema değişiklikleri içerdiği durumlarda, Oda uygun bir taşıma yolunu otomatik olarak oluşturabilir. Örneğin, bir tablodaki verileri iki tabloya bölmeye karar verdiniz. Oda, nasıl yapılacağını göstereceğim. Bu gibi durumlarda şunu uygulayarak bir taşıma yolu tanımlayın: Migration sınıfı.

Migration sınıfı, geçersiz kılarak startVersion ve endVersion Migration.migrate() yöntemidir. Migration sınıflarınızı veritabanı oluşturucunuza eklemek için şunu kullanın: "the" addMigrations() yöntem:

Kotlin

val MIGRATION_1_2 = object : Migration(1, 2) {
  override fun migrate(database: SupportSQLiteDatabase) {
    database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, `name` TEXT, " +
      "PRIMARY KEY(`id`))")
  }
}

val MIGRATION_2_3 = object : Migration(2, 3) {
  override fun migrate(database: SupportSQLiteDatabase) {
    database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")
  }
}

Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name")
  .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build()

Java

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
  @Override
  public void migrate(SupportSQLiteDatabase database) {
    database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "
      + "`name` TEXT, PRIMARY KEY(`id`))");
  }
};

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
  @Override
  public void migrate(SupportSQLiteDatabase database) {
    database.execSQL("ALTER TABLE Book "
      + " ADD COLUMN pub_year INTEGER");
  }
};

Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
  .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();

Taşıma yollarınızı tanımlarken bazı taşıma işlemleri için otomatik taşıma diğer kullanıcılar için manuel taşıma yapabilirsiniz. Hem otomatik hem de için manuel taşıma gerçekleştirir. Ardından Oda, manuel olarak taşıma.

Taşıma işlemlerini test etme

Taşıma işlemleri genellikle karmaşıktır ve yanlış tanımlanan bir taşıma işlemi kilitlenmesine neden olabilir. Uygulamanızın kararlılığını korumak için taşıma işlemleri. Oda, şu işlemlerde yardımcı olmak için bir room-testing Maven yapısı sağlar: test süreci için de kullanılabilir. Bu yapı için veritabanınızın şemasını dışa aktarmanız gerekir.

Şemaları dışa aktarma

Oda, veritabanınızın şema bilgilerini derleme sırasında bir JSON dosyasına aktarabilir gerekir. Dışa aktarılan JSON dosyaları, veritabanınızın şema geçmişini temsil eder. Mağaza bu dosyaları sürüm kontrol sisteminizde kullanabilirsiniz. Böylece Oda, veritabanını test etmek ve otomatik taşıma oluşturmayı etkinleştirmek için kullanabilirsiniz.

Room Gradle eklentisini kullanarak şema konumunu ayarlama

Oda 2.6.0 veya daha yeni bir sürüm kullanıyorsanız Room Gradle Eklentisi'ni ziyaret edin ve Şema dizinini belirten room uzantısı.

Eski

plugins {
  id 'androidx.room'
}

room {
  schemaDirectory "$projectDir/schemas"
}

Kotlin

plugins {
  id("androidx.room")
}

room {
  schemaDirectory("$projectDir/schemas")
}

Veritabanı şemanız varyant, tür veya derlemeye göre farklılık gösteriyorsa kullanmak istiyorsanız, schemaDirectory() öğesini kullanarak farklı konumlar belirtmeniz gerekir birden fazla kez yapılandır ve her biri ilk sırada variantMatchName olacak şekilde bağımsız değişkeninin önüne geçer. Her yapılandırma, modelin basit değerine göre bir veya daha fazla varyantla eşleşebilir karşılaştırabilirsiniz.

Bu koşulların kapsamlı olduğundan ve tüm varyantları kapsadığından emin olun. İsterseniz Eşleşmeyen varyantları işlemek için variantMatchName içermeyen schemaDirectory() değiştirebilirsiniz. Örneğin, iki derleme içeren bir uygulamada demo ve full aromaları ile debug ve release iki derleme türü, Aşağıdaki geçerli yapılandırmalar verilmiştir:

Eski

room {
  // Applies to 'demoDebug' only
  schemaDirectory "demoDebug", "$projectDir/schemas/demoDebug"

  // Applies to 'demoDebug' and 'demoRelease'
  schemaDirectory "demo", "$projectDir/schemas/demo"

  // Applies to 'demoDebug' and 'fullDebug'
  schemaDirectory "debug", "$projectDir/schemas/debug"

  // Applies to variants that aren't matched by other configurations.
  schemaDirectory "$projectDir/schemas"
}

Kotlin

room {
  // Applies to 'demoDebug' only
  schemaDirectory("demoDebug", "$projectDir/schemas/demoDebug")

  // Applies to 'demoDebug' and 'demoRelease'
  schemaDirectory("demo", "$projectDir/schemas/demo")

  // Applies to 'demoDebug' and 'fullDebug'
  schemaDirectory("debug", "$projectDir/schemas/debug")

  // Applies to variants that aren't matched by other configurations.
  schemaDirectory("$projectDir/schemas")
}

Ek açıklama işlemci seçeneğini kullanarak şema konumunu ayarlama

Room'un 2.5.2 veya daha eski bir sürümünü kullanıyorsanız ya da Room Gradle Eklentisi, şema konumunu room.schemaLocation kullanarak ayarlayın ek açıklama işlemcisi seçeneği olarak tanımlanmalıdır.

Bu dizindeki dosyalar bazı Gradle görevleri için giriş ve çıkış olarak kullanılır. Artımlı ve önbelleğe alınmış derlemelerin doğruluğu ve performansı için Gradle's CommandLineArgumentProvider Gradle'ı bu dizin hakkında bilgilendirdim.

İlk olarak, aşağıda gösterilen RoomSchemaArgProvider sınıfını modülünüzün'e kopyalayın. Gradle derleme dosyası. Örnek sınıftaki asArguments() yöntemi başarılı room.schemaLocation=${schemaDir.path} - KSP. KAPT ve javac yerine bu değeri -Aroom.schemaLocation=${schemaDir.path} olarak değiştirin.

Eski

class RoomSchemaArgProvider implements CommandLineArgumentProvider {

  @InputDirectory
  @PathSensitive(PathSensitivity.RELATIVE)
  File schemaDir

  RoomSchemaArgProvider(File schemaDir) {
    this.schemaDir = schemaDir
  }

  @Override
  Iterable<String> asArguments() {
    // Note: If you're using KAPT and javac, change the line below to
    // return ["-Aroom.schemaLocation=${schemaDir.path}".toString()].
    return ["room.schemaLocation=${schemaDir.path}".toString()]
  }
}

Kotlin

class RoomSchemaArgProvider(
  @get:InputDirectory
  @get:PathSensitive(PathSensitivity.RELATIVE)
  val schemaDir: File
) : CommandLineArgumentProvider {

  override fun asArguments(): Iterable<String> {
    // Note: If you're using KAPT and javac, change the line below to
    // return listOf("-Aroom.schemaLocation=${schemaDir.path}").
    return listOf("room.schemaLocation=${schemaDir.path}")
  }
}

Ardından derleme seçeneklerini, RoomSchemaArgProvider öğesini belirtilen şema dizini:

Eski

// For KSP, configure using KSP extension:
ksp {
  arg(new RoomSchemaArgProvider(new File(projectDir, "schemas")))
}

// For javac or KAPT, configure using android DSL:
android {
  ...
  defaultConfig {
    javaCompileOptions {
      annotationProcessorOptions {
        compilerArgumentProviders(
          new RoomSchemaArgProvider(new File(projectDir, "schemas"))
        )
      }
    }
  }
}

Kotlin

// For KSP, configure using KSP extension:
ksp {
  arg(RoomSchemaArgProvider(File(projectDir, "schemas")))
}

// For javac or KAPT, configure using android DSL:
android {
  ...
  defaultConfig {
    javaCompileOptions {
      annotationProcessorOptions {
        compilerArgumentProviders(
          RoomSchemaArgProvider(File(projectDir, "schemas"))
        )
      }
    }
  }
}

Tek bir taşıma işlemini test etme

Taşıma işlemlerinizi test etmeden önce Oda'daki androidx.room:room-testing Maven yapısı testinize eklendi ve dışa aktarılan şemanın konumunu bir öğe klasörü olarak ekleyin:

derleme.gradle

Eski

android {
    ...
    sourceSets {
        // Adds exported schema location as test app assets.
        androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
    }
}

dependencies {
    ...
    androidTestImplementation "androidx.room:room-testing:2.6.1"
}

Kotlin

android {
    ...
    sourceSets {
        // Adds exported schema location as test app assets.
        getByName("androidTest").assets.srcDir("$projectDir/schemas")
    }
}

dependencies {
    ...
    testImplementation("androidx.room:room-testing:2.6.1")
}

Test paketi, MigrationTestHelper sınıfını kullanır. Paket ayrıca JÜnite4 TestRule arayüzü sayesinde, oluşturulan veritabanlarını yönetebilir.

Aşağıdaki örnekte tek bir taşıma işlemine yönelik bir test gösterilmektedir:

Kotlin

@RunWith(AndroidJUnit4::class)
class MigrationTest {
    private val TEST_DB = "migration-test"

    @get:Rule
    val helper: MigrationTestHelper = MigrationTestHelper(
            InstrumentationRegistry.getInstrumentation(),
            MigrationDb::class.java.canonicalName,
            FrameworkSQLiteOpenHelperFactory()
    )

    @Test
    @Throws(IOException::class)
    fun migrate1To2() {
        var db = helper.createDatabase(TEST_DB, 1).apply {
            // Database has schema version 1. Insert some data using SQL queries.
            // You can't use DAO classes because they expect the latest schema.
            execSQL(...)

            // Prepare for the next version.
            close()
        }

        // Re-open the database with version 2 and provide
        // MIGRATION_1_2 as the migration process.
        db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)

        // MigrationTestHelper automatically verifies the schema changes,
        // but you need to validate that the data was migrated properly.
    }
}

Java

@RunWith(AndroidJUnit4.class)
public class MigrationTest {
    private static final String TEST_DB = "migration-test";

    @Rule
    public MigrationTestHelper helper;

    public MigrationTest() {
        helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
                MigrationDb.class.getCanonicalName(),
                new FrameworkSQLiteOpenHelperFactory());
    }

    @Test
    public void migrate1To2() throws IOException {
        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);

        // Database has schema version 1. Insert some data using SQL queries.
        // You can't use DAO classes because they expect the latest schema.
        db.execSQL(...);

        // Prepare for the next version.
        db.close();

        // Re-open the database with version 2 and provide
        // MIGRATION_1_2 as the migration process.
        db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2);

        // MigrationTestHelper automatically verifies the schema changes,
        // but you need to validate that the data was migrated properly.
    }
}

Tüm taşıma işlemlerini test et

Tek bir artımlı taşımayı test etmek mümkün olsa da uygulamanız için tanımlanan tüm taşıma işlemlerini kapsayan bir test eklemeniz Bu, yakın zamanda oluşturulan ve en son yayınlanan ve tanımlanan taşıma işlemini izleyen daha eski bir örnek yollar.

Aşağıdaki örnekte tanımlanan tüm taşıma işlemlerine yönelik bir test gösterilmektedir:

Kotlin

@RunWith(AndroidJUnit4::class)
class MigrationTest {
    private val TEST_DB = "migration-test"

    // Array of all migrations.
    private val ALL_MIGRATIONS = arrayOf(
            MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)

    @get:Rule
    val helper: MigrationTestHelper = MigrationTestHelper(
            InstrumentationRegistry.getInstrumentation(),
            AppDatabase::class.java.canonicalName,
            FrameworkSQLiteOpenHelperFactory()
    )

    @Test
    @Throws(IOException::class)
    fun migrateAll() {
        // Create earliest version of the database.
        helper.createDatabase(TEST_DB, 1).apply {
            close()
        }

        // Open latest version of the database. Room validates the schema
        // once all migrations execute.
        Room.databaseBuilder(
            InstrumentationRegistry.getInstrumentation().targetContext,
            AppDatabase::class.java,
            TEST_DB
        ).addMigrations(*ALL_MIGRATIONS).build().apply {
            openHelper.writableDatabase.close()
        }
    }
}

Java

@RunWith(AndroidJUnit4.class)
public class MigrationTest {
    private static final String TEST_DB = "migration-test";

    @Rule
    public MigrationTestHelper helper;

    public MigrationTest() {
        helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
                AppDatabase.class.getCanonicalName(),
                new FrameworkSQLiteOpenHelperFactory());
    }

    @Test
    public void migrateAll() throws IOException {
        // Create earliest version of the database.
        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
        db.close();

        // Open latest version of the database. Room validates the schema
        // once all migrations execute.
        AppDatabase appDb = Room.databaseBuilder(
                InstrumentationRegistry.getInstrumentation().getTargetContext(),
                AppDatabase.class,
                TEST_DB)
                .addMigrations(ALL_MIGRATIONS).build();
        appDb.getOpenHelper().getWritableDatabase();
        appDb.close();
    }

    // Array of all migrations.
    private static final Migration[] ALL_MIGRATIONS = new Migration[]{
            MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4};
}

Eksik taşıma yollarının sorunsuz bir şekilde ele alınması

Oda, mevcut bir veritabanını cihazın mevcut sürümüne geçirerek IllegalStateException gerçekleşir. Eğer bir taşıma yolu eksik olduğunda mevcut verilerin kaybedilmesi kabul edilebilir, "the" fallbackToDestructiveMigration() oluşturucu yöntemini eklemeniz gerekir:

Kotlin

Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name")
        .fallbackToDestructiveMigration()
        .build()

Java

Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
        .fallbackToDestructiveMigration()
        .build();

Bu yöntem, Room'a uygulamanızın veri tabanınız için artımlı bir taşıma gerçekleştirmesi gerektiğinde ve tanımlanmış taşıma yolunu izleyin.

Yalnızca bazı durumlarda yıkıcı eğlence faaliyetlerinin tekrarlanmasını istiyorsanız durumlarda, fallbackToDestructiveMigration() yerine birkaç alternatif vardır:

  • Şema geçmişinizin belirli sürümleri, çözemediğiniz hatalara neden oluyorsa taşıma yollarında fallbackToDestructiveMigrationFrom() . Bu yöntem, Oda'nın yıkıcı öğelere dönmesini istediğinizi belirtir yalnızca belirli sürümlerden taşınırken yeniden oluşturma.
  • Odanın yalnızca taşıma sırasında yıkıcı yeniden oluşturma işlemlerine yedeklenmesini istiyorsanız daha düşük bir sürüme geçmek için fallbackToDestructiveMigrationOnDowngrade() .
ziyaret edin.

Oda 2.2.0'a yükseltme yaparken sütun varsayılan değerlerini işleme

Oda 2.2.0 ve sonraki sürümlerde, bir sütun için ek açıklama @ColumnInfo(defaultValue = "..."). 2.2.0'dan önceki sürümlerde, bir sütunu, doğrudan yürütülen bir SQL deyimi içinde tanımlanarak odanın bilmediği varsayılan değere sahiptir. Bu, veri tabanının İlk olarak, Odanızın 2.2.0'dan daha eski bir sürümü tarafından oluşturulmuş, uygulamanızı Oda 2.2.0'ı kullanmak için Oda API'lerini kullanmadan tanımladığınız mevcut varsayılan değerlerdir.

Örneğin, veritabanı sürüm 1'in bir Song varlığını tanımladığını varsayalım:

Kotlin

// Song entity, database version 1, Room 2.1.0.
@Entity
data class Song(
    @PrimaryKey
    val id: Long,
    val title: String
)

Java

// Song entity, database version 1, Room 2.1.0.
@Entity
public class Song {
    @PrimaryKey
    final long id;
    final String title;
}

Aynı veritabanına ait sürüm 2'nin, yeni bir NOT NULL sütunu eklediğini varsayalım ve sürüm 1'den sürüm 2'ye bir taşıma yolunu tanımlar:

Kotlin

// Song entity, database version 2, Room 2.1.0.
@Entity
data class Song(
    @PrimaryKey
    val id: Long,
    val title: String,
    val tag: String // Added in version 2.
)

// Migration from 1 to 2, Room 2.1.0.
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL(
            "ALTER TABLE Song ADD COLUMN tag TEXT NOT NULL DEFAULT ''")
    }
}

Java

// Song entity, database version 2, Room 2.1.0.
@Entity
public class Song {
    @PrimaryKey
    final long id;
    final String title;
    @NonNull
    final String tag; // Added in version 2.
}


// Migration from 1 to 2, Room 2.1.0.
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL(
            "ALTER TABLE Song ADD COLUMN tag TEXT NOT NULL DEFAULT ''");
    }
};

Bu durum, temel tabloda güncellemeler ile yeni alanlar arasında farklılığa neden olur. en az 24 saat sürer. Çünkü tag sütunu için varsayılan değer yalnızca sürüm 1'den sürüm 2'ye geçiş yolunda belirtilen tüm kullanıcıları uygulamayı sürüm 2'den itibaren yüklediğinizde tag için varsayılan değer bulunmuyor eklememiz gerekir.

Odanın 2.2.0'dan önceki sürümlerinde, bu tutarsızlık zararsızdır. Ancak, Uygulama daha sonra Oda 2.2.0 veya sonraki bir sürümü kullanacak şekilde yükseltilir ve Song varlığını değiştirir sınıfını kullanarak tag için varsayılan bir değer eklemek üzere @ColumnInfo ek açıklaması, Oda bu tutarsızlığı görebilirsiniz. Bu durum, şemanın başarısız olmasına yol açar doğrulamaları gerekir.

Sütun aşağıdaki durumlarda veritabanı şemasının tüm kullanıcılar arasında tutarlı olduğundan emin olmak için: önceki taşıma yollarınızda bildirilen varsayılan değerler için aşağıdakileri yapın: Oda 2.2.0 veya sonraki bir sürümü kullanmak için uygulamanızı ilk kez yükselttiğinizde:

  1. Aşağıdaki komutu kullanarak ilgili varlık sınıflarındaki sütun varsayılan değerlerini bildirin: @ColumnInfo ek açıklaması.
  2. Veritabanı sürüm numarasını 1 artırın.
  3. Drop ve stratejiyi yeniden oluşturma değerini girin.
ziyaret edin.

Aşağıdaki örnekte bu işlem gösterilmektedir:

Kotlin

// Migration from 2 to 3, Room 2.2.0.
val MIGRATION_2_3 = object : Migration(2, 3) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("""
                CREATE TABLE new_Song (
                    id INTEGER PRIMARY KEY NOT NULL,
                    name TEXT,
                    tag TEXT NOT NULL DEFAULT ''
                )
                """.trimIndent())
        database.execSQL("""
                INSERT INTO new_Song (id, name, tag)
                SELECT id, name, tag FROM Song
                """.trimIndent())
        database.execSQL("DROP TABLE Song")
        database.execSQL("ALTER TABLE new_Song RENAME TO Song")
    }
}

Java

// Migration from 2 to 3, Room 2.2.0.
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("CREATE TABLE new_Song (" +
                "id INTEGER PRIMARY KEY NOT NULL," +
                "name TEXT," +
                "tag TEXT NOT NULL DEFAULT '')");
        database.execSQL("INSERT INTO new_Song (id, name, tag) " +
                "SELECT id, name, tag FROM Song");
        database.execSQL("DROP TABLE Song");
        database.execSQL("ALTER TABLE new_Song RENAME TO Song");
    }
};