Android: DB sürümünü yükseltme ve yeni tablo ekleme


118

Uygulamam için zaten sqlite tabloları oluşturdum, ancak şimdi veritabanına yeni bir tablo eklemek istiyorum.

DB sürümünü aşağıdaki gibi değiştirdim

private static final int DATABASE_VERSION = 2;

ve tablo oluşturmak için dize eklendi

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

onCreateve onUpgradeaşağıdaki gibi:

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

    }

Ancak bazı nedenlerden dolayı yeni tablo oluşturulmuyor. Neyi yanlış yapıyorum?


Bu başka bir ilginç çözüm, ancak şimdiye kadar gördüğüm en sağlam sürüm burada .
Suragch

Yanıtlar:


280

1. onCreate () ve onUpgrade () Hakkında

onCreate(..)uygulama her yeni yüklendiğinde çağrılır. onUpgradeuygulama her yükseltilip başlatıldığında ve veritabanı sürümü aynı olmadığında çağrılır .

2. db sürümünü yükseltme

Aşağıdaki gibi bir kurucuya ihtiyacınız var:

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

ÖNEMLİ: Uygulama sürümünün yükseltilmesi tek başına onUpgradeçağrılmak için yeterli değildir !

3. Yeni kullanıcılarınızı unutmayın!

Eklemeyi unutma

database.execSQL(DATABASE_CREATE_color);

onCreate () yönteminize veya yeni yüklenen uygulamalarda tablo bulunmayacaktır.

4. Zaman içinde birden çok veritabanı değişikliğiyle nasıl başa çıkılır

Birçoğunda veritabanı yükseltmeleri olan art arda uygulama yükseltmeleriniz olduğunda, şunları kontrol ettiğinizden emin olmak istersiniz 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); 
   }
}

Bu şekilde, bir kullanıcı sürüm 1'den sürüm 3'e yükseltme yaptığında, her iki güncellemeyi de alır. Bir kullanıcı sürüm 2'den 3'e yükseltme yaptığında, sadece revizyon 3 güncellemesini alır ... Sonuçta, her güncelleme yayınladığınızda yükseltme yapmak için kullanıcı tabanınızın% 100'üne güvenemezsiniz. Bazen bir güncellemeyi atlarlar veya 12 :)

5. Geliştirme sırasında revizyon numaralarınızı kontrol altında tutmak

Ve sonunda ...

adb uninstall <yourpackagename>

uygulamayı tamamen kaldırır. Tekrar kurduğunuzda, onCreatesizi geliştirirken veritabanı sürümünü stratosfere yükseltmeye devam etmek zorunda kalmamanızı garanti altına alırsınız ...


5
# 4 ile ilgili olarak: Aktarılan oldVersionargümanı kullanmak daha iyi bir fikir olmaz mıydı ? Herhangi bir yükseltme ifadesi tekrarlanabilirse, bunları çoğunlukla güncel bir veritabanında tekrarlayabilirsiniz. İfadelerden biri bir tabloyu kısaltmaksa, bu çok kötü olur.
Greyson

3
@Greyson: Harika nokta! Açıkçası, bunu hiç düşünmediğim için kendimi biraz aptal hissediyorum. Bazen istediğimiz argümanları kullanma ve geri kalanını görmezden gelme alışkanlığı edindiğimizi düşünüyorum!
jkschneider

1
Veritabanını kontrol ediyorsunuz, adı neden değiştiriyorsunuz?
jkschneider

3
newVersionzaten yapıcıda her zaman geçerli veritabanı sürümünü ayarladığınız için (bkz. bölüm 2) ve her zaman eşleşeceği için bir nevi faydasızdır. Buradaki temel fikir, kullanıcının newVersionaradaki diğer tüm artımlı yükseltmelerden geçmeden doğrudan bulunduğu yerden yükseltme yapmak istememenizdir .
jkschneider

2
@kai CREATE_READINGSMantık onCreate, ilk sürümünüzün yönteminde olduğu için asla onUpgrade'de olmamalıdır . onUpgradeAnahtardaki vakaları "ŞİMDEN yükseltiyorum" olarak düşünün oldVersion. Zaten olması gerektiği için, sürüm 1'den yükseltme yapıyorsanız, okuma tablosunu oluşturmazsınız. Umarım bu mantıklıdır ...
jkschneider

9

Kodunuz doğru görünüyor. Benim önerim, veritabanının zaten yükseltilmiş olduğunu düşünüyor olmasıdır. Projeyi sürüm numarasını artırdıktan sonra, ancak execSQLaramayı eklemeden önce çalıştırdıysanız, test cihazınızdaki / emülatörünüzdeki veritabanı sürüm 2'de olduğuna zaten inanıyor olabilir.

Bunu doğrulamanın hızlı bir yolu, sürüm numarasını 3 olarak değiştirmek olacaktır - bundan sonra yükseltilirse, bunun yalnızca cihazınızın zaten yükseltilmiş olduğuna inandığından kaynaklandığını bilirsiniz.


Sonra, beklendiği gibi, kodunuz iyiydi; artımlı olarak çalıştırıldığında değil. Tablo oluşturmayı onCreate()jkschneider'ın işaret ettiği gibi eklemeyi unutmayın.
Greyson

2

SQLiteOpenHelper onUpgradeyöntemini kullanabilirsiniz . OnUpgrade yönteminde, oldVersion'ı parametrelerden biri olarak alırsınız.

Olarak onUpgradekullanımı a switchve hercase sinde, veritabanının mevcut sürümünü takip etmek için sürüm numarasını kullanın.

En iyisi her yerinden size döngü o oldVersionkadar newVersion, artanversion bir seferde 1 ile ve daha sonra adım veritabanı adım yükseltin. Bu, veritabanı sürümü 1'e sahip bir kişi uygulamayı uzun bir süre sonra veritabanı sürüm 7'yi kullanan bir sürüme yükselttiğinde ve bazı uyumsuz değişiklikler nedeniyle uygulama çökmeye başladığında çok yararlıdır.

Ardından, veritabanındaki güncellemeler, tüm olası durumları kapsayacak şekilde, yani her yeni sürüm için yapılan veritabanındaki değişiklikleri dahil ederek ve böylece uygulamanızın çökmesini önleyerek adım adım yapılacaktır.

Örneğin:

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;
    }

}

Bu mola ifadelerini kaldırırsanız, bir döngüye ihtiyacınız olmayacak
Tash Pemhiwa

ancak oldVersion bir sonraki vakayı geçmek için her durumda artırılmalıdır @TashPemhiwa
Beulah Ana

Switch ifadesinin ara vermeyi gerektirmesinin nedeni, aynı anda birden fazla vakayı çalıştırmanın mümkün olmasıdır - ve vaka koşulu karşılanmasa bile durum böyle olacaktır, @BeulahAna
Tash Pemhiwa

Break eklerseniz ve bazı db'ler eski veya yeni sürüme sahipse, sorgunuz başarısız olabilir, bu nedenle break gerekli değildir, örneğin bazı db sürümlerinde bir sütun zaten değişiyorsa, sorgunuz db sürümünün kayıp sırasına göre başarısız olabilir
Neeraj Singh

2

@ jkschneider'ın cevabı doğru. Ancak daha iyi bir yaklaşım var.

Https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/ bağlantısında açıklandığı gibi her güncelleme için gerekli değişiklikleri bir sql dosyasına yazın.

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) ;

Bu .sql dosyaları, veritabanının sürümüne göre onUpgrade () yönteminde çalıştırılacaktır.

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

Aynı bağlantıda örnek bir proje de verilmiştir: https://github.com/riggaroo/AndroidDatabaseUpgrades


1
Ben de tam buraya gelip aynı tavsiyeyi yazmak üzereydim. Bunu zaten yaptığına sevindim. İnsanlar bağlantı verdiğiniz makaleyi kesinlikle okumalı. Bu aynı zamanda Android SQLiteAssetHelper'ın yükseltmeler için önerdiği şeydir . Aynı zamanda CL. ( Burada Yığın taşması SQLite uzmanı) önerir .
Suragch

Aradığım bu yorum. SQL betikleri, +1
blueware

1

Veritabanı sürümlerini işlemek, uygulama geliştirmenin çok önemli bir parçasıdır. Zaten AppDbHelper sınıfına sahip olduğunuzu varsayıyorum SQLiteOpenHelper. Uzattığınızda uygulamanız onCreateve onUpgradeyöntem yapmanız gerekecektir .

  1. Ne zaman onCreateve onUpgradeyöntemler çağrıldı

    • onCreate uygulama yeni yüklendiğinde çağrılır.
    • onUpgrade uygulama güncellendiğinde çağrılır.
  2. Veritabanı sürümlerini organize etmek Bir sınıf yöntemindeki sürümleri yönetiyorum. Arayüz Geçişinin uygulamasını oluşturun. Örneğin, ilk sürüm oluşturma MigrationV1sınıfı için, ikinci sürüm oluşturma MigrationV1ToV2(bunlar benim adlandırma kuralımdır)


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

Örnek taşıma:

public class MigrationV1ToV2 implements Migration{
      public void run(SQLiteDatabase db){
        //create new tables
        //alter existing tables(add column, add/remove constraint)
        //etc.
     }
   }
  1. Geçiş sınıflarını kullanma

onCreate: onCreateUygulama yeni kurulduğunda çağrılacağından, tüm geçişleri de (veritabanı sürümü güncellemeleri) yürütmemiz gerekir. Öyleyse onCreateşöyle görünecek:

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: Bu yöntem, uygulama zaten yüklendiğinde ve yeni uygulama sürümüne güncellendiğinde çağrılacaktır. Uygulama herhangi bir veritabanı değişikliği içeriyorsa, tüm veritabanı değişikliklerini yeni Migration sınıfına koyun ve veritabanı sürümünü artırın.

Örneğin, kullanıcının veritabanı sürümü 1 olan uygulamayı yüklediğini ve şimdi veritabanı sürümünün 2 olarak güncellendiğini (tüm şema güncellemeleri içinde tutulur MigrationV1ToV2) varsayalım . Şimdi uygulama yükseltildiğinde, aşağıdaki MigrationV1ToV2gibi veritabanı şeması değişikliklerini uygulayarak veritabanını yükseltmemiz gerekiyor :

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
    }
}

Not: onUpgradeVeritabanı şemasında belirtilen tüm yükseltmeler ,onCreate

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.