SQLiteOpenHelper onCreate () / onUpgrade () ne zaman çalışır?


294

Tablolarımı kendimde oluşturdum SQLiteOpenHelper onCreate()ama alıyorum

SQLiteException: no such table

veya

SQLiteException: no such column

hatalar. Neden?

NOT:

(Bu, her hafta onlarca benzer sorunun bir araya getirilmiş özetidir. Tüm bu soruların iyi bir referansa yönlendirilebilmesi için burada "kanonik" bir topluluk wiki sorusu / yanıtı sağlamaya çalışmak.)


12
@Ndupza Bu benim gerçek bir sorun değil, sadece Nth kez aynı cevabı / yorumu yazmaktan bıktım.
laalto

Yanıtlar:


353

SQLiteOpenHelper onCreate()ve onUpgrade()veritabanı gerçekten açıldığında geri çağrılar çağrılır, örneğin bir çağrı ile getWritableDatabase(). Veritabanı yardımcı nesnesinin kendisi oluşturulduğunda veritabanı açılmaz.

SQLiteOpenHelperveritabanı dosyaları sürümleri. Sürüm numarasıdır intgeçirilen argümanı yapıcısı . Veritabanı dosyasında sürüm numarası depolanır PRAGMA user_version.

onCreate()yalnızca veritabanı dosyası mevcut olmadığında ve yeni oluşturulduğunda çalıştırılır. Eğer onCreate()başarılı döner (bir istisna değil), veritabanı istenen sürüm numarasına sahip oluşturulacak varsayılır. Bir ima olarak, yakalamak olmamalıdır SQLExceptioniçinde s onCreate()kendin.

onUpgrade()yalnızca veritabanı dosyası mevcutsa ancak depolanan sürüm numarası yapıcıda istenenden düşük olduğunda çağrılır. onUpgrade()Talep edilen sürüme tablo şemasını güncelleştirmek gerekir.

Code ( onCreate()) içindeki tablo şemasını değiştirirken, veritabanının güncellendiğinden emin olmalısınız. İki ana yaklaşım:

  1. onCreate()Yeniden çalıştırılacak şekilde eski veritabanı dosyasını silin . Bu, yüklü sürümler üzerinde kontrol sahibi olduğunuz ve veri kaybının sorun olmadığı geliştirme zamanında tercih edilir. Veritabanı dosyasını silmenin bazı yolları:

    • Uygulamayı kaldırın. Uygulama yöneticisini veya adb uninstall your.package.namekabuktan kullanın.

    • Uygulama verilerini temizle. Uygulama yöneticisini kullanın.

  2. Veritabanı sürümünü onUpgrade()çağrılacak şekilde artırın. Daha fazla kod gerektiğinden bu biraz daha karmaşıktır.

    • Veri kaybının bir sorun olmadığı geliştirme zaman şeması yükseltmeleri için, sadece execSQL("DROP TABLE IF EXISTS <tablename>")mevcut tablolarınızı kaldırmak onCreate()ve veritabanını yeniden oluşturmak için kullanabilirsiniz.

    • Yayınlanan sürümlerde, onUpgrade()kullanıcılarınızın verilerini kaybetmemesi için veri taşıma işlemini uygulamalısınız .


2
@Laalto // onUpgrade'de veri taşıma () // Lütfen bunu açıklayabilir misiniz?
bCliks

2
@bala Bu soru / cevap kapsamında değil. Bir sorunuz varsa, soru olarak göndermekten çekinmeyin.
laalto

2
@Jaskey Sürüm numarası kodunuz içindir; yani kodun hangi şema sürümüne karşı çalışacağını bekler. Dosya eskiyse (uygulamanızın önceki bir sürümünden), yeni sürüme geçirilmesi gerekir.
laalto

4
Bu nedenle, şemayı her değiştirdiğimde SQL VERSİYONU DB VERSION kodunu sabit kodlamalıyım, böylece eski uygulama çalıştığında ve db bağlantısını alıp eski olduğunda ve onUpgrade yerine onCreate yerine trgiigered olacak, bu sağ?
Jaskey

2
Teşekkür ederim ! Bu bana mantıklı geliyor. Lütfen iyi anladığımı doğrulayın. Bu yüzden şemayı her güncellediğimizde DB_VERSION değişkenini (sabit kod) değiştirin. 2. 'de onUpdate(), her eski sürümü kontrol edin ve doğru veri geçişini yapın. Ve sonra bir kullanıcı uygulamasını güncellediğinde (eski db dosyaları var), onUpgradeTetiklenir ve kullanıcı yeni yüklüyse onCreate()tetiklenir.
Jaskey

97

Jaskey'in isteğine göre, eksik noktaları daha fazla eklemek için

Veritabanı sürümü, SQLiteveritabanı dosyasında saklanır .

catch yapıcı

SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

Dolayısıyla, veritabanı yardımcı kurucusu bir name(2. param) ile çağrıldığında , platform veritabanının var olup olmadığını kontrol eder ve veritabanı varsa, sürüm bilgilerini veritabanı dosya başlığından alır ve doğru geri çağrıyı tetikler

Eski yanıtta daha önce açıklandığı gibi, adı olan veritabanı yoksa, tetiklenir onCreate.

Aşağıdaki açıklama onUpgradeörneği bir örnekle açıklamaktadır .

Diyelim ki, uygulamanızın ilk sürümünde yapıcı geçiş sürümü olarak DatabaseHelper(genişletme SQLiteOpenHelper) vardı 1ve daha sonra yeni kaynak kodunun sürümü geçmiş olarak yükseltilmiş bir uygulama sağladınız 2, daha sonra DatabaseHelperoluşturulduğunda platform onUpgradezaten dosyayı görerek otomatik olarak tetikleniyor , ancak sürüm, geçtiğiniz geçerli sürümden daha düşük.

Şimdi db sürümü ile uygulamanın üçüncü bir sürümünü vermeyi planladığınızı varsayalım 3(db sürümü yalnızca veritabanı şeması değiştirilecekse artar). Bu tür artımlı yükseltmelerde, daha iyi korunabilir bir kod için yükseltme mantığını her sürümden kademeli olarak yazmanız gerekir.

Aşağıdaki örnek sahte kod:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  switch(oldVersion) {
    case 1:
       //upgrade logic from version 1 to 2
    case 2:
       //upgrade logic from version 2 to 3
    case 3:
       //upgrade logic from version 3 to 4
       break;
    default:
       throw new IllegalStateException(
                "onUpgrade() with unknown oldVersion " + oldVersion);
  }
}

Durumunda eksik breakifadeye dikkat edin 1ve 2. Kademeli yükseltme ile kastediyorum.

Eski versiyon ise Say 2ve yeni versiyonudur 4, daha sonra mantık gelen veritabanı yükseltir 2için 3ve daha sonra4

Eski sürüm ise 3ve yeni sürüm ise 4, yalnızca yükseltme mantığını çalıştıracaktır 3.4


1
Anahtarınızın (newVersion) anahtarının (oldVersion) olmasını istediğinizi düşünüyorum. Ayrıca, newVersion'un 4 (5 veya 3 değil) olduğunu doğrulamak isteyebilirsiniz; çünkü mantığınız yeni sürümün 4 olması gerektiğini varsayar. Eski sürüm 2 ve yeni sürüm 5 ise, Vaka 4'e basın: ve 3'ten 4'e yükseltin (muhtemelen beklenen davranış olmamalıdır).
joe p

sağ - yazım hatası .. ama yeni sürüm 5 ise -> o zaman her zaman IllegalStateException atar ve geliştirici durum 5 ekleyerek düzeltiyor olacak ..
Aun

1
Kullanıcı uygulamasını yalnızca sürüm 2'den 3'e yükseltirse ne olur? Bu durumda da, dava 4'e kadar olan tüm davalar çalışacaktır.
Paramvir Singh

6
@param kullanıcısı bunu yapamaz. Sadece 2'yi en son sürümüne (burada 4) yükseltebilir.
Habeeb Perwad

20

onCreate()

  1. Biz ilk seferde DataBase oluşturduğunuzda (yani Veritabanı Varlığından değildir) onCreate()içinde geçirilen sürümüyle veritabanı oluşturmak SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

  2. onCreate()yöntemi, tanımladığınız tabloları oluşturmak ve yazdığınız diğer kodları çalıştırmaktır. Ancak, bu yöntem yalnızca uygulamanızın veri dizininde ( /data/data/your.apps.classpath/databases) SQLite dosyası eksikse çağrılır .

  3. Kodunuzu değiştirdiyseniz ve emülatörde yeniden başlattıysanız bu yöntem çağrılmaz. İsterseniz onCreate()çalıştırmak için size SQLite veritabanı dosyasını silmek için adb kullanmak gerekir.

onUpgrade()

  1. SQLiteOpenHelper süper kurucuyu çağırmalıdır.
  2. onUpgrade()Versiyon tamsayı uygulamasında çalışan mevcut sürümü daha büyük olduğunda, yöntem, yalnızca çağrılır.
  3. İsterseniz onUpgrade()yöntem çağrılacak, kodunuzda Sürüm numarasını artırmayı gerekir.

1
Verdiğiniz çözüm hakkında biraz daha açıklama ekleyerek cevabınızı biraz daha açıklayabilir misiniz?
abarisone

10

Belki çok geç kaldım ama kısa ve tatlı cevabımı paylaşmak istiyorum. Kontrol edin Cevap aynı sorun için. Kesinlikle size yardımcı olacaktır. Daha derin özellikler yok.

Tablo oluşturmak için sözdiziminden eminseniz, aynı tablonuza yeni sütun eklediğinizde olabileceğinden, bunun için ...

1) Cihazınızdan kaldırın ve tekrar çalıştırın.

VEYA

2) Ayar -> uygulama -> ClearData

VEYA

3) Değişim DATABASE_VERSIONsizin "DatabaseHandler" sınıfında (eğer yeni bir sütun ekledinizse otomatik olarak yükseltilir yerine)

public DatabaseHandler(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

VEYA

4) Değişim DATABASE_NAMEsizin "DatabaseHandler" sınıfında (Ben de aynı sorunla karşı karşıya. Ama değiştirerek başarılı DATABASE_NAME.)


Kendi DB ve SQLiteAssetHelper sınıfı kullanarak var. Böylece, önce sql komut dosyası ile DB oluşturdunuz ve db oluşturuldu. SQLiteAssetHelper kullanarak, emülatör veya cihazdan uygulama kaldırılana kadar DB'yi kopyalayamadı, çünkü aynı sürüme sahip bir db idi.
Behzad

4

Genişletirken hatırlanması gereken noktalar SQLiteOpenHelper

  1. super(context, DBName, null, DBversion); - Bu ilk yapıcı satırı çağrılmalıdır
  2. geçersiz kıl onCreateve onUpgrade(gerekirse)
  3. onCreateyalnızca getWritableDatabase()veya getReadableDatabase()yürütüldüğünde çağrılır . Ve bu sadece DBNameilk adımda belirtilen bir bilgi mevcut olmadığında çağrılır . onCreateYönteme tablo sorgusu ekleyebilirsiniz
  4. Yeni tablo eklemek istediğinizde, yalnızca tablodaki DBversionsorguları değiştirin ve yapın onUpgradeveya uygulamayı kaldırın.

3

tabloların oluşturulması gerektiğinde ilk kez onCreate çağrılır. SQLiteDatabase tarafından yürütülen tablo oluşturma komut dosyasını yazdığımız bu yöntemi geçersiz kılmamız gerekir. execSQL yöntemi. İlk dağıtımda yürütüldükten sonra, bu yöntem daha sonra çağrılmaz.

onUpgrade Bu yöntem, veritabanı sürümü yükseltildiğinde çağrılır. İlk dağıtım için veritabanı sürümü 1 ve ikinci dağıtımda veritabanı yapısında tabloya fazladan sütun ekleme gibi bir değişiklik olduğunu varsayalım. Diyelim ki veritabanı sürümü 2.


2

Gibi veritabanı ve tablo oluşturabilirsiniz

public class DbHelper extends SQLiteOpenHelper {
private static final String DBNAME = "testdatbase.db";
private static final int VERSION = 1;

public DbHelper(Context context) {
    super(context, DBNAME, null, VERSION);
    // TODO Auto-generated constructor stub
}

@Override
public void onCreate(SQLiteDatabase db) {
    // TODO Auto-generated method stub
    db.execSQL("create table BookDb(id integer primary key autoincrement,BookName text,Author text,IssuedOn text,DueDate text,Fine text,Totalfine text");

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS BookDb");
    onCreate(db);
  }
}

Not: Başka bir tablo oluşturmak veya sütun eklemek veya böyle bir tablo eklemek istemiyorsanız, SÜRÜMÜ artırın


2

SQLite veritabanı iki yöntemi geçersiz kılar

1) onCreate (): Bu yöntem, uygulama ilk kez başlatıldığında yalnızca bir kez çağrılır. Bu yüzden sadece bir kez çağırdı

2) onUpgrade () Bu yöntem, veritabanı sürümünü değiştirdiğimizde çağrılır, daha sonra bu yöntemler çağrılır. DB Şeması oluşturduktan sonra yeni sütun ekleme gibi tablo yapısını değiştirmek için kullanılır


1

böyle bir tablo bulundu esas olarak SQLiteOpenHelpersınıf ile açmadıysanız getwritabledata()ve bundan önce de veritabanınıenadı ve sürümü ile make yapıcı çağırmak zorunda. Ve OnUpgradeverilen sürüm numarasını yükseltme değer olduğunda denir SQLiteOpenHelpersınıfın.

Kod snippet'i aşağıdadır (Böyle bir sütun, sütun adındaki yazım nedeniyle olmayabilir):

public class database_db {
    entry_data endb;
    String file_name="Record.db";
    SQLiteDatabase sq;
    public database_db(Context c)
    {
        endb=new entry_data(c, file_name, null, 8);
    }
    public database_db open()
    {
        sq=endb.getWritableDatabase();
        return this;
    }
    public Cursor getdata(String table)
    {
        return sq.query(table, null, null, null, null, null, null);
    }
    public long insert_data(String table,ContentValues value)
    {
        return sq.insert(table, null, value);
    }
    public void close()
    {
        sq.close();
    }
    public void delete(String table)
    {
        sq.delete(table,null,null);
    }
}
class entry_data extends SQLiteOpenHelper
{

    public entry_data(Context context, String name, SQLiteDatabase.CursorFactory factory,
                      int version) {
        super(context, name, factory, version);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onCreate(SQLiteDatabase sqdb) {
        // TODO Auto-generated method stub

        sqdb.execSQL("CREATE TABLE IF NOT EXISTS 'YOUR_TABLE_NAME'(Column_1 text not null,Column_2 text not null);");

    }

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
          onCreate(db);
    }

}

1

Yapıcıya ikinci argüman olarak bir "ad" dizesi sağlamayı unutursanız, uygulamayı kapattığınızda silinecek bir "bellek içi" veritabanı oluşturur.


0

Uygulamanızı öykünücüden veya cihazdan kaldırın. Uygulamayı tekrar çalıştırın. (Veritabanı zaten varken OnCreate () yürütülmez)


0

Veritabanı adınız .db ile bitmeli ve sorgu dizelerinizde bir sonlandırıcı (;) bulunmalıdır


0

Sorgunuzu ur DatabaseHandler / DatabaseManager sınıfında tekrar kontrol edin (hangisini aldınız)


0

Benim durumumda XML dosyasından öğeler <string-array>alıyorum <item>. Bu <item>s ben SQL dizeleri tutun ve ile tek tek uygulayın databaseBuilder.addMigrations(migration). Bir hata yaptım, \alıntıdan önce eklemeyi unuttum ve istisna aldım:

android.database.sqlite.SQLiteException: böyle bir sütun yok: some_value (kod 1 SQLITE_ERROR): derlerken: table_name (id, name) VALUES (1, some_value)

Yani, bu doğru bir varyant:

<item>
    INSERT INTO table_name(id, name) VALUES(1, \"some_value\")
</item>

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.