Kiedy jest uruchamiany SQLiteOpenHelper onCreate () / onUpgrade ()?


293

Utworzyłem swoje tabele w moim, SQLiteOpenHelper onCreate()ale otrzymuję

SQLiteException: no such table

lub

SQLiteException: no such column

błędy. Dlaczego?

UWAGA:

(To jest połączone podsumowanie dziesiątek podobnych pytań co tydzień. Próba dostarczenia „kanonicznego” pytania / odpowiedzi na wiki społeczności, aby wszystkie te pytania mogły zostać skierowane do dobrego źródła).


12
@Ndupza To nie jest mój problem, po prostu mam dość pisania tej samej odpowiedzi / komentarza po raz piąty.
laalto

Odpowiedzi:


352

SQLiteOpenHelper onCreate()a onUpgrade()wywołania zwrotne są wywoływane podczas faktycznego otwarcia bazy danych, na przykład przez wywołanie do getWritableDatabase(). Baza danych nie jest otwierana podczas tworzenia samego obiektu pomocnika bazy danych.

SQLiteOpenHelperwersje plików bazy danych. Numer wersji jest intargumentem przekazywanym do konstruktora . W pliku bazy danych numer wersji jest zapisany w PRAGMA user_version.

onCreate()jest uruchamiany tylko wtedy, gdy plik bazy danych nie istniał i został właśnie utworzony. Jeśli onCreate()zwraca pomyślnie (nie zgłasza wyjątku), zakłada się, że baza danych została utworzona z żądanym numerem wersji. Implikacją jest to, że nie powinieneś łapać się SQLExceptionw onCreate()sobie.

onUpgrade()jest wywoływany tylko wtedy, gdy plik bazy danych istnieje, ale zapisany numer wersji jest niższy niż wymagany w konstruktorze. onUpgrade()Powinien zaktualizować schematu tabeli do wersji wniosku.

Zmieniając schemat tabeli w code ( onCreate()), należy upewnić się, że baza danych jest zaktualizowana. Dwa główne podejścia:

  1. Usuń stary plik bazy danych, aby onCreate()został ponownie uruchomiony. Jest to często preferowane w czasie programowania, gdy masz kontrolę nad zainstalowanymi wersjami, a utrata danych nie stanowi problemu. Niektóre sposoby usunięcia pliku bazy danych:

    • Odinstaluj aplikację. Użyj menedżera aplikacji lub adb uninstall your.package.namez powłoki.

    • Wyczyść dane aplikacji. Użyj menedżera aplikacji.

  2. Zwiększ wersję bazy danych, aby onUpgrade()została wywołana. Jest to nieco bardziej skomplikowane, ponieważ potrzeba więcej kodu.

    • W przypadku aktualizacji schematu czasu programowania, w których utrata danych nie stanowi problemu, można po prostu użyć execSQL("DROP TABLE IF EXISTS <tablename>")do usunięcia istniejących tabel i wywołać onCreate()odtworzenie bazy danych.

    • W wydanych wersjach należy wdrożyć migrację danych, onUpgrade()aby użytkownicy nie stracili swoich danych.


2
@Laalto // migracja danych w onUpgrade () // Czy możesz to wyjaśnić.
bCliks

2
@bala Nie wchodzi w zakres tego pytania / odpowiedzi. Jeśli masz pytanie, możesz je zadać jako pytanie.
laalto

2
@Jaskey Numer wersji dotyczy Twojego kodu, tj. Jakiej wersji schematu oczekuje kod. Jeśli plik jest starszy (z poprzedniej wersji aplikacji), należy go zaktualizować.
laalto

4
Więc muszę na stałe kodować WERSJĘ DB w SQLiteHelper za każdym razem, gdy modyfikuję schemat, aby po uruchomieniu starej aplikacji i uzyskaniu połączenia z bazą danych i stwierdzeniu, że jest stara, a następnie onUpgrade zostanie wyzwolony zamiast onCreate, czy to dobrze?
Jaskey,

2
Dziękuję Ci ! Ma to dla mnie sens. Sprawdź, czy dobrze rozumiem. Więc musimy zrobić 1. za każdym razem, gdy aktualizujemy schemat, zmodyfikuj zmienną DB_VERSION (twardy kod). 2. W onUpdate(), sprawdź każdą starą wersję i wykonaj właściwą migrację danych. A następnie, gdy użytkownik zaktualizuje swoją aplikację (mają stare pliki db), onUpgradezostanie onCreate()wyzwolony , a jeśli użytkownik zostanie nowo zainstalowany, zostanie wyzwolony.
Jaskey,

97

Aby dodać tutaj brakujące punkty, zgodnie z prośbą Jaskeya

Wersja bazy danych jest przechowywana w SQLitepliku bazy danych.

catch jest konstruktorem

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

Tak więc gdy wywoływany jest konstruktor pomocnika bazy danych z name(2. parametr), platforma sprawdza, czy baza danych istnieje, czy nie, a jeśli baza istnieje, pobiera informacje o wersji z nagłówka pliku bazy danych i wyzwala właściwe oddzwonienie

Jak już wyjaśniono w starszej odpowiedzi, jeśli baza danych o nazwie nie istnieje, uruchamia się onCreate.

Poniżej wyjaśnienie wyjaśnia onUpgradeprzypadek na przykładzie.

Powiedzmy, że Twoja pierwsza wersja aplikacji miała DatabaseHelper (rozszerzającą się SQLiteOpenHelper) wersję konstruktora przekazującą as, 1a następnie dostarczyłeś uaktualnioną aplikację z nowym kodem źródłowym, którego wersja została przekazana jako 2, a następnie automatycznie po DatabaseHelperzbudowaniu platforma uruchamia onUpgradesię, widząc, że plik już istnieje, ale wersja jest niższa niż bieżąca wersja, którą przeszedłeś.

Powiedzmy teraz, że planujesz podać trzecią wersję aplikacji w wersji db jako 3(wersja db jest zwiększana tylko wtedy, gdy schemat bazy danych ma zostać zmodyfikowany). W takich przyrostowych aktualizacjach musisz zapisywać logikę aktualizacji z każdej wersji przyrostowo, aby uzyskać łatwiejszy do utrzymania kod

Przykładowy pseudo kod poniżej:

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

Zauważ brak break instrukcję w przypadku 1i 2. To właśnie rozumiem przez przyrostową aktualizację.

Powiedz, czy stara wersja jest, 2a nowa wersja jest4 , to logika zaktualizuje bazę danych z 2do, 3a następnie do4

Jeśli stara wersja 3i nowa wersja jest 4, to wystarczy uruchomić aktualizację logikę 3do4


1
Myślę, że zamiast tego chcesz, aby twój przełącznik (nowa wersja) był przełącznikiem (stara wersja). Możesz także sprawdzić, czy newVersion ma wartość 4 (a nie 5 lub 3; ponieważ logika zakłada, że ​​nowa wersja powinna mieć wartość 4). Tak jak w przypadku starej wersji 2, a nowej wersji 5, traf przypadek 4: i uaktualnij z 3 do 4 (czego prawdopodobnie nie należy się spodziewać).
Joe P

prawo - literówka .. ale jeśli nowa wersja to 5 ->, to zawsze wyrzuci IllegalStateException, a programista naprawi ją, dodając przypadek 5 ..
Aun

1
Co się stanie, jeśli użytkownik zaktualizuje swoją aplikację tylko z wersji 2 do 3? W takim przypadku uruchomione zostaną wszystkie przypadki aż do przypadku 4.
Paramvir Singh

6
Użytkownik @param nie może tego zrobić. Może ulepszyć tylko 2 do najnowszych (tutaj 4).
Habeeb Perwad

20

onCreate()

  1. Kiedy tworzymy bazę danych po raz pierwszy (tzn. Baza danych nie istnieje), onCreate()utwórz bazę danych z przekazaną wersją SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

  2. onCreate()Metoda polega na utworzeniu tabel, które zdefiniowałeś i wykonaniu dowolnego napisanego kodu. Jednak ta metoda zostanie wywołana tylko wtedy, gdy brakuje pliku SQLite w katalogu danych aplikacji ( /data/data/your.apps.classpath/databases).

  3. Ta metoda nie zostanie wywołana, jeśli zmieniłeś kod i ponownie uruchomiłeś emulator. Jeśli chcesz onCreate()uruchomić, musisz użyć adb, aby usunąć plik bazy danych SQLite.

onUpgrade()

  1. SQLiteOpenHelper powinien wywołać superkonstruktora.
  2. The onUpgrade()Metoda zostanie wywołana tylko wtedy, gdy wersja liczba całkowita jest większa od bieżącej wersji działającej w aplikacji.
  3. Jeśli chcesz onUpgrade()wywołać metodę, musisz zwiększyć numer wersji w kodzie.

1
Czy mógłby Pan uściślić swoją odpowiedź, dodając nieco więcej opisu oferowanego rozwiązania?
abarisone

10

Być może spóźniłem się, ale chciałbym podzielić się moją krótką i słodką odpowiedzią. Sprawdź odpowiedź na ten sam problem. To na pewno ci pomoże. Nigdy więcej szczegółowych specyfikacji.

Jeśli masz pewność co do składni tworzenia tabeli, to może się zdarzyć, gdy dodasz nową kolumnę w tej samej tabeli, dlatego ...

1) Odinstaluj urządzenie i uruchom je ponownie.

LUB

2) Ustawienia -> aplikacja -> ClearData

LUB

3) Zmiana DATABASE_VERSIONw klasie „DatabaseHandler” (jeśli dodałeś nową kolumnę, nastąpi automatyczna aktualizacja)

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

LUB

4) Zmiana DATABASE_NAMEw klasie „DatabaseHandler” (napotkałem ten sam problem. Ale zmieniam się DATABASE_NAME.)


Mam własną bazę danych i używam klasy SQLiteAssetHelper. Tak więc wcześniej utworzyłem skrypt DB za pomocą sql i db został utworzony. Korzystając z SQLiteAssetHelper, nie mógł skopiować bazy danych do momentu odinstalowania aplikacji z emulatora lub urządzenia, ponieważ była to baza danych z tą samą wersją.
Behzad

4

Punkty do zapamiętania przy przedłużaniu SQLiteOpenHelper

  1. super(context, DBName, null, DBversion); - Należy wywołać pierwszą linię konstruktora
  2. zastąpić onCreatei onUpgrade(w razie potrzeby)
  3. onCreatebędą wywoływane tylko po wykonaniu getWritableDatabase()lub getReadableDatabase(). I to zostanie wywołane tylko raz, gdy DBNameokreślony w pierwszym kroku nie będzie dostępny. Możesz dodać opcję tworzenia zapytania tabeli w onCreatemetodzie
  4. Ilekroć chcesz dodać nową tabelę, po prostu zmień DBversioni wykonaj zapytania w onUpgradetabeli lub po prostu odinstaluj, a następnie zainstaluj aplikację.

3

Program onCreate jest wywoływany po raz pierwszy, gdy potrzebne jest tworzenie tabel. Musimy zastąpić tę metodę, w której piszemy skrypt do tworzenia tabeli, który jest wykonywany przez SQLiteDatabase. metoda execSQL. Po pierwszym wdrożeniu metoda ta nie będzie wywoływana dalej.

onUpgrade Ta metoda jest wywoływana przy aktualizacji wersji bazy danych. Załóżmy, że przy pierwszym wdrożeniu wersja bazy danych miała numer 1, a przy drugim wdrożeniu nastąpiła zmiana struktury bazy danych, np. Dodanie dodatkowej kolumny w tabeli. Załóżmy, że wersja bazy danych ma teraz 2.


2

Możesz stworzyć bazę danych i tabelę

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

Uwaga: jeśli chcesz utworzyć inną tabelę lub dodać kolumny lub nie ma takiej tabeli, po prostu zwiększ WERSJĘ


2

Baza danych Sqlite przesłania dwie metody

1) onCreate (): Ta metoda została wywołana tylko raz przy pierwszym uruchomieniu aplikacji. Więc zadzwonił tylko raz

2) onUpgrade () Ta metoda wywoływana podczas zmiany wersji bazy danych, a następnie wywoływana jest ta metoda. Jest używana do zmiany struktury tabeli, np. Dodawania nowej kolumny po utworzeniu schematu DB


1

taka tabela nie została znaleziona głównie wtedy, gdy nie otworzyłeś SQLiteOpenHelperklasy, getwritabledata()a przed tym musisz także wywołać make constructor z nazwą bazy danych i wersją. I OnUpgradejest wywoływany, ilekroć w wersji podana jest wartość aktualizacji podana w SQLiteOpenHelperklasie.

Poniżej znajduje się fragment kodu (nie znaleziono takiej kolumny z powodu pisowni w nazwie kolumny):

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

Jeśli zapomnisz podać ciąg „nazwa” jako drugi argument konstruktora, utworzy on bazę danych „w pamięci”, która zostanie wymazana po zamknięciu aplikacji.


0

Odinstaluj aplikację z emulatora lub urządzenia. Uruchom aplikację ponownie. (OnCreate () nie jest wykonywany, gdy baza danych już istnieje)


0

Nazwa bazy danych musi kończyć się .db, a także ciągi zapytań muszą mieć terminator (;)


0

Sprawdź ponownie zapytanie w swojej klasie DatabaseHandler / DatabaseManager (którą kiedykolwiek wziąłeś)


0

W moim przypadku otrzymuję elementy z pliku XML <string-array>, w którym przechowuję <item>s. W tych <item>trzymam ciągi SQL i stosuję je pojedynczo databaseBuilder.addMigrations(migration). Popełniłem jeden błąd, zapomniałem dodać\ przed cytatem i dostałem wyjątek:

android.database.sqlite.SQLiteException: brak takiej kolumny: some_value (kod 1 SQLITE_ERROR):, podczas kompilacji: INSERT INTO nazwa_tabeli (id, nazwa) VALUES (1, some_value)

To jest właściwy wariant:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.