Android: mengupgrade versi DB dan menambahkan tabel baru

117

Saya sudah membuat tabel sqlite untuk aplikasi saya, tetapi sekarang saya ingin menambahkan tabel baru ke database.

Saya mengubah versi DB seperti di bawah ini

private static final int DATABASE_VERSION = 2;

dan Menambahkan string untuk membuat tabel

private static final String DATABASE_CREATE_color = 
   "CREATE TABLE IF NOT EXISTS files(color text, incident_id text)";

onCreatedan onUpgradeseperti di bawah ini:

@Override
    public void onCreate(SQLiteDatabase database) {
        database.execSQL(DATABASE_CREATE_incident);
        database.execSQL(DATABASE_CREATE_audio);
        database.execSQL(DATABASE_CREATE_video);
        database.execSQL(DATABASE_CREATE_image);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //drop table and add new tables when version 2 released.
        db.execSQL(DATABASE_CREATE_color);

    }

Tetapi untuk beberapa alasan tabel baru tidak dibuat. Apa yang saya lakukan salah?

Jay Mayu
sumber
Ini adalah solusi menarik lainnya, tetapi sejauh ini versi paling kuat yang pernah saya lihat ada di sini .
Suragch

Jawaban:

280

1. Tentang onCreate () dan onUpgrade ()

onCreate(..)dipanggil setiap kali aplikasi baru saja dipasang. onUpgradedipanggil setiap kali aplikasi ditingkatkan dan diluncurkan dan versi database tidak sama.

2. Meningkatkan versi db

Anda membutuhkan konstruktor seperti:

MyOpenHelper(Context context) {
   super(context, "dbname", null, 2); // 2 is the database version
}

PENTING: Hanya menaikkan versi aplikasi saja tidak cukup untuk onUpgradedipanggil!

3. Jangan lupakan pengguna baru Anda!

Jangan lupa tambahkan

database.execSQL(DATABASE_CREATE_color);

ke metode onCreate () Anda juga atau aplikasi yang baru diinstal tidak akan memiliki tabel.

4. Bagaimana menangani beberapa perubahan database dari waktu ke waktu

Saat Anda memiliki peningkatan aplikasi berturut-turut, beberapa di antaranya memiliki peningkatan basis data, Anda ingin memastikan untuk memeriksa oldVersion:

onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   switch(oldVersion) {
   case 1:
       db.execSQL(DATABASE_CREATE_color);
       // we want both updates, so no break statement here...
   case 2:
       db.execSQL(DATABASE_CREATE_someothertable); 
   }
}

Dengan cara ini ketika pengguna meningkatkan dari versi 1 ke versi 3, mereka mendapatkan kedua pembaruan. Ketika seorang pengguna meningkatkan dari versi 2 ke 3, mereka hanya mendapatkan pembaruan revisi 3 ... Lagi pula, Anda tidak dapat mengandalkan 100% basis pengguna Anda untuk meningkatkan setiap kali Anda merilis pembaruan. Terkadang mereka melewatkan pembaruan atau 12 :)

5. Menjaga nomor revisi Anda terkendali saat mengembangkan

Dan akhirnya ... menelepon

adb uninstall <yourpackagename>

benar-benar mencopot pemasangan aplikasi. Ketika Anda menginstal lagi, Anda dijamin akan mencapai onCreateyang membuat Anda tidak perlu terus menaikkan versi database ke stratosfer saat Anda mengembangkan ...

jkschneider
sumber
5
Mengenai # 4: Bukankah lebih baik menggunakan oldVersionargumen yang disampaikan? Jika ada pernyataan pemutakhiran yang dapat diulang, Anda mungkin akhirnya mengulanginya pada database yang sebagian besar terbaru. Jika salah satu pernyataan memotong tabel, itu akan sangat buruk.
Greyson
3
@Greyson: Poin yang bagus! Sejujurnya, saya merasa agak bodoh karena tidak pernah benar-benar memikirkannya. Terkadang saya berpikir kita terbiasa menggunakan argumen yang kita inginkan dan mengabaikan sisanya!
jkschneider
1
Anda mengontrol database, mengapa Anda mengubah namanya?
jkschneider
3
newVersionagak tidak berguna, karena Anda selalu menetapkan versi database saat ini dalam konstruktor (lihat bagian 2) dan versi ini akan selalu cocok. Ide utamanya di sini adalah Anda tidak ingin hanya meningkatkan dari mana pun pengguna langsung newVersiontanpa melewati setiap peningkatan tambahan lainnya di antaranya.
jkschneider
2
@kai CREATE_READINGSLogika tidak boleh ada di onUpgrade, karena ini adalah onCreatemetode versi pertama Anda. Pikirkan kasus di onUpgradesakelar sebagai "Saya meningkatkan DARI oldVersion". Anda tidak akan membuat tabel bacaan jika Anda meningkatkan dari versi 1, karena seharusnya sudah ada. Mudah-mudahan ini masuk akal ...
jkschneider
9

Kode Anda sudah benar. Saran saya adalah database sudah mengira itu ditingkatkan. Jika Anda menjalankan proyek setelah menambah nomor versi, tetapi sebelum menambahkan execSQLpanggilan, database di perangkat pengujian / emulator Anda mungkin sudah percaya bahwa itu ada di versi 2.

Cara cepat untuk memverifikasi ini adalah dengan mengubah nomor versi menjadi 3 - jika ditingkatkan setelah itu, Anda tahu itu hanya karena perangkat Anda yakin itu sudah ditingkatkan.

Greyson
sumber
Kemudian, seperti yang diharapkan, kode Anda baik-baik saja; hanya saja tidak saat dijalankan secara bertahap. Ingatlah untuk menambahkan pembuatan tabel onCreate()seperti yang ditunjukkan jkschneider.
Greyson
2

Anda dapat menggunakan onUpgrademetode SQLiteOpenHelper . Dalam metode onUpgrade, Anda mendapatkan oldVersion sebagai salah satu parameter.

Dalam onUpgradepenggunaan a switchdan di masing-masing casegunakan nomor versi untuk melacak versi database saat ini.

Yang terbaik adalah Anda mengulang dari oldVersionke newVersion, menambah version1 pada satu waktu lalu memutakhirkan database selangkah demi selangkah. Ini sangat membantu ketika seseorang dengan database versi 1 mengupgrade aplikasi setelah waktu yang lama, ke versi yang menggunakan database versi 7 dan aplikasi mulai mogok karena perubahan tertentu yang tidak kompatibel.

Kemudian pembaruan dalam basis data akan dilakukan secara bertahap, mencakup semua kasus yang mungkin, yaitu memasukkan perubahan dalam basis data yang dilakukan untuk setiap versi baru dan dengan demikian mencegah aplikasi Anda mogok.

Sebagai contoh:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion) {
    case 1:
        String sql = "ALTER TABLE " + TABLE_SECRET + " ADD COLUMN " + "name_of_column_to_be_added" + " INTEGER";
        db.execSQL(sql);
        break;

    case 2:
        String sql = "SOME_QUERY";
        db.execSQL(sql);
        break;
    }

}
Vijesh Jat
sumber
Jika Anda menghapus pernyataan istirahat itu, Anda tidak memerlukan loop
Tash Pemhiwa
tetapi oldVersion harus bertambah di setiap kasus untuk melewati kasus berikutnya @TashPemhiwa
Beulah Ana
Alasan pernyataan peralihan memerlukan jeda adalah karena dimungkinkan untuk menjalankan banyak kasus sekaligus - dan ini akan tetap berlaku meskipun kondisi kasus tidak terpenuhi, @BeulahAna
Tash Pemhiwa
Jika Anda menambahkan break dan beberapa db memiliki versi lama atau terbaru maka kueri Anda dapat gagal sehingga tidak diperlukan break. Contoh mengubah tabel jika beberapa kolom sudah berubah di beberapa versi db maka kueri Anda mungkin gagal sesuai urutan kerugian versi db
Neeraj Singh
2

Jawaban @ jkschneider benar. Namun ada pendekatan yang lebih baik.

Tulis perubahan yang diperlukan dalam file sql untuk setiap pembaruan seperti yang dijelaskan di tautan https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/

from_1_to_2.sql

ALTER TABLE books ADD COLUMN book_rating INTEGER;

from_2_to_3.sql

ALTER TABLE books RENAME TO book_information;

from_3_to_4.sql

ALTER TABLE book_information ADD COLUMN calculated_pages_times_rating INTEGER;
UPDATE book_information SET calculated_pages_times_rating = (book_pages * book_rating) ;

File .sql ini akan dijalankan dalam metode onUpgrade () sesuai dengan versi database.

DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final int DATABASE_VERSION = 4;

    private static final String DATABASE_NAME = "database.db";
    private static final String TAG = DatabaseHelper.class.getName();

    private static DatabaseHelper mInstance = null;
    private final Context context;

    private DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    public static synchronized DatabaseHelper getInstance(Context ctx) {
        if (mInstance == null) {
            mInstance = new DatabaseHelper(ctx.getApplicationContext());
        }
        return mInstance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(BookEntry.SQL_CREATE_BOOK_ENTRY_TABLE);
        // The rest of your create scripts go here.

    }


    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.e(TAG, "Updating table from " + oldVersion + " to " + newVersion);
        // You will not need to modify this unless you need to do some android specific things.
        // When upgrading the database, all you need to do is add a file to the assets folder and name it:
        // from_1_to_2.sql with the version that you are upgrading to as the last version.
        try {
            for (int i = oldVersion; i < newVersion; ++i) {
                String migrationName = String.format("from_%d_to_%d.sql", i, (i + 1));
                Log.d(TAG, "Looking for migration file: " + migrationName);
                readAndExecuteSQLScript(db, context, migrationName);
            }
        } catch (Exception exception) {
            Log.e(TAG, "Exception running upgrade script:", exception);
        }

    }

    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    private void readAndExecuteSQLScript(SQLiteDatabase db, Context ctx, String fileName) {
        if (TextUtils.isEmpty(fileName)) {
            Log.d(TAG, "SQL script file name is empty");
            return;
        }

        Log.d(TAG, "Script found. Executing...");
        AssetManager assetManager = ctx.getAssets();
        BufferedReader reader = null;

        try {
            InputStream is = assetManager.open(fileName);
            InputStreamReader isr = new InputStreamReader(is);
            reader = new BufferedReader(isr);
            executeSQLScript(db, reader);
        } catch (IOException e) {
            Log.e(TAG, "IOException:", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    Log.e(TAG, "IOException:", e);
                }
            }
        }

    }

    private void executeSQLScript(SQLiteDatabase db, BufferedReader reader) throws IOException {
        String line;
        StringBuilder statement = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            statement.append(line);
            statement.append("\n");
            if (line.endsWith(";")) {
                db.execSQL(statement.toString());
                statement = new StringBuilder();
            }
        }
    }
}

Contoh proyek juga disediakan di tautan yang sama: https://github.com/riggaroo/AndroidDatabaseUpgrades

oiyio
sumber
1
Saya baru saja akan datang ke sini dan menulis nasihat yang sama. Saya senang Anda sudah melakukannya. Orang pasti harus membaca artikel yang Anda tautkan. Ini juga yang direkomendasikan Android SQLiteAssetHelper untuk peningkatan. Itu juga yang CL. ( Yang ahli SQLite sini di Stack Overflow) merekomendasikan .
Suragch
Ini komentar yang saya cari. Skrip sql, +1
blueware
1

Menangani versi database adalah bagian yang sangat penting dalam pengembangan aplikasi. Saya berasumsi bahwa Anda sudah memiliki kelas AppDbHelper yang diperluas SQLiteOpenHelper. Saat Anda memperpanjangnya, Anda perlu mengimplementasikan onCreatedan onUpgrademetode.

  1. Kapan onCreatedan onUpgrademetode dipanggil

    • onCreate dipanggil saat aplikasi baru dipasang.
    • onUpgrade dipanggil saat aplikasi diperbarui.
  2. Mengorganisir versi Database Saya mengelola versi dalam metode kelas. Buat implementasi Migrasi antarmuka. Misalnya untuk versi pertama buat MigrationV1kelas, buat versi kedua MigrationV1ToV2(ini adalah konvensi penamaan saya)


    public interface Migration {
        void run(SQLiteDatabase db);//create tables, alter tables
    }

Contoh migrasi:

public class MigrationV1ToV2 implements Migration{
      public void run(SQLiteDatabase db){
        //create new tables
        //alter existing tables(add column, add/remove constraint)
        //etc.
     }
   }
  1. Menggunakan kelas Migrasi

onCreate: Sejak onCreate akan dipanggil ketika aplikasi baru saja diinstal, kami juga perlu menjalankan semua migrasi (pembaruan versi database). Jadi onCreateakan terlihat seperti ini:

public void onCreate(SQLiteDatabase db){
        Migration mV1=new MigrationV1();
       //put your first database schema in this class
        mV1.run(db);
        Migration mV1ToV2=new MigrationV1ToV2();
        mV1ToV2.run(db);
        //other migration if any
  }

onUpgrade: Metode ini akan dipanggil ketika aplikasi sudah diinstal dan diperbarui ke versi aplikasi baru. Jika aplikasi berisi perubahan database apa pun, letakkan semua perubahan database di kelas Migrasi baru dan versi database tambahan.

Misalnya, pengguna telah menginstal aplikasi yang memiliki database versi 1, dan sekarang versi database diperbarui ke 2 (semua pembaruan skema disimpan MigrationV1ToV2). Sekarang ketika aplikasi diupgrade, kita perlu mengupgrade database dengan menerapkan perubahan skema database MigrationV1ToV2seperti ini:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (oldVersion < 2) {
        //means old version is 1
        Migration migration = new MigrationV1ToV2();
        migration.run(db);
    }
    if (oldVersion < 3) {
        //means old version is 2
    }
}

Catatan: Semua pemutakhiran (disebutkan dalam onUpgrade) ke skema basis data harus dijalankan dionCreate

VIjay J
sumber