Quando o SQLiteOpenHelper onCreate () / onUpgrade () é executado?

293

Eu criei minhas tabelas no meu SQLiteOpenHelper onCreate()mas recebo

SQLiteException: no such table

ou

SQLiteException: no such column

erros. Por quê?

NOTA:

(Este é o resumo amalgamado de dezenas de perguntas semelhantes toda semana. Tentativa de fornecer aqui uma pergunta / resposta "canônica" do wiki da comunidade, para que todas essas perguntas possam ser direcionadas para uma boa referência.)

laalto
fonte
12
@Ndupza Este não é um problema meu, apenas farto de escrever a mesma resposta / comentário pela enésima vez.
21414 laalto

Respostas:

352

SQLiteOpenHelper onCreate()e onUpgrade()retornos de chamada são chamados quando o banco de dados é realmente aberto, por exemplo, por uma chamada para getWritableDatabase(). O banco de dados não é aberto quando o próprio objeto auxiliar do banco de dados é criado.

SQLiteOpenHelperversões dos arquivos de banco de dados. O número da versão é o intargumento passado ao construtor . No arquivo de banco de dados, o número da versão é armazenado PRAGMA user_version.

onCreate()é executado apenas quando o arquivo do banco de dados não existia e foi criado. Se onCreate()retornar com êxito (não lança uma exceção), presume-se que o banco de dados seja criado com o número da versão solicitada. Como implicação, você não deve pegar SQLExceptions em onCreate()si mesmo.

onUpgrade()é chamado apenas quando o arquivo de banco de dados existe, mas o número da versão armazenada é menor que o solicitado no construtor. oonUpgrade() deve atualizar o esquema da tabela para a versão solicitada.

Ao alterar o esquema da tabela no código ( onCreate()), verifique se o banco de dados está atualizado. Duas abordagens principais:

  1. Exclua o arquivo de banco de dados antigo para que onCreate()seja executado novamente. Isso geralmente é preferido no momento do desenvolvimento, onde você tem controle sobre as versões instaladas e a perda de dados não é um problema. Algumas maneiras de excluir o arquivo de banco de dados:

    • Desinstale o aplicativo. Use o gerenciador de aplicativos ou a adb uninstall your.package.namepartir do shell.

    • Limpar dados do aplicativo. Use o gerenciador de aplicativos.

  2. Incremente a versão do banco de dados para que onUpgrade()seja chamada. Isso é um pouco mais complicado, pois é necessário mais código.

    • Para atualizações de esquema em tempo de desenvolvimento em que a perda de dados não é um problema, você pode apenas usar execSQL("DROP TABLE IF EXISTS <tablename>")para remover suas tabelas existentes e chamar onCreate()para recriar o banco de dados.

    • Para versões lançadas, você deve implementar a migração de dados onUpgrade()para que seus usuários não percam seus dados.

laalto
fonte
2
@Laalto // migração de dados em onUpgrade () // Você pode explicar isso?
bCliks
2
@bala Não está no escopo desta pergunta / resposta. Se você tiver uma pergunta, sinta-se à vontade para publicá-la como uma pergunta.
precisa saber é
2
@ Jaskey O número da versão é para o seu código, ou seja, em qual versão do esquema o código espera rodar. Se o arquivo for mais antigo (de uma versão anterior do seu aplicativo), ele precisará ser atualizado.
Lahor 13/10
4
Portanto, preciso codificar a versão do banco de dados no SQLiteHelper toda vez que modifico o esquema, para que, quando o aplicativo antigo for executado, obtenha a conexão db e a encontre antiga, e o onUpgrade seja trgiigered em vez de onCreate. certo?
Jaskey
2
Obrigado ! Isso faz sentido para mim. Verifique se entendi bem. Então, precisamos fazer 1. sempre que atualizarmos o esquema, modifique a variável DB_VERSION (código rígido). 2. onUpdate()Verifique todas as versões antigas e faça a migração de dados adequada. E então, quando um usuário atualizar seu aplicativo (ele possui arquivos db antigos), onUpgradeserá acionado e, se o usuário for instalado recentemente, onCreate()será acionado.
Jaskey
97

Para adicionar ainda mais pontos ausentes aqui, conforme solicitação de Jaskey

A versão do banco de dados é armazenada no diretório SQLite arquivo de banco de dados.

catch é o construtor

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

Portanto, quando o construtor auxiliar do banco de dados é chamado com um name (2º param), a plataforma verifica se o banco de dados existe ou não e se o banco de dados existe, ele obtém as informações da versão do cabeçalho do arquivo do banco de dados e aciona a chamada correta

Como já explicado na resposta antiga, se o banco de dados com o nome não existir, ele será acionado onCreate.

A explicação abaixo explica o onUpgradecaso com um exemplo.

Digamos, sua primeira versão do aplicativo teve a versão de DatabaseHelperextensão do SQLiteOpenHelperconstrutor (estendendo-se ) 1e, em seguida, você forneceu um aplicativo atualizado com o novo código-fonte com a versão passada e 2, automaticamente, quando a DatabaseHelperplataforma é construída, é acionadaonUpgrade ao ver o arquivo já existe, mas a versão é inferior à versão atual que você passou.

Agora diga que você está planejando fornecer uma terceira versão do aplicativo com a versão db como 3(a versão db é aumentada apenas quando o esquema do banco de dados deve ser modificado). Nessas atualizações incrementais, você deve escrever a lógica de atualização de cada versão de forma incremental para obter um código melhor de manutenção

Exemplo de pseudo-código abaixo:

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

Observe a breakdeclaração ausente no caso 1e 2. É isso que quero dizer com atualização incremental.

Diga se a versão antiga é 2e a nova é 4, a lógica atualizará o banco de dados de 2para 3e depois para4

Se a versão antiga é 3e nova versão é 4, ela só vai executar a lógica de atualização para 3a4

Aun
fonte
1
Eu acho que você deseja que o seu switch (newVersion) seja switch (oldVersion). Você também pode verificar se newVersion é 4 (e não 5 ou 3; porque sua lógica está assumindo que a nova versão deve ser 4). Assim, se a versão antiga for 2 e a nova versão for 5, você acerte o caso 4: e atualize de 3 para 4 (o que provavelmente não deve ser o comportamento esperado).
joe p
certo - erro de digitação .. mas se a nova versão for 5 -> ela sempre lançará IllegalStateException e o desenvolvedor a corrigirá adicionando o caso 5 .. #
23614
1
E se o usuário atualizar seu aplicativo apenas da versão 2 para 3? Também nesse caso, todos os casos até o caso 4 serão executados.
Paramvir Singh
6
O usuário do @param não pode fazer isso. Ele pode atualizar 2 para o mais recente (aqui 4) apenas.
Habeeb Perwad
20

onCreate()

  1. Quando criamos o DataBase pela primeira vez (ou seja, o banco de dados não existe), onCreate()criamos o banco de dados com a versão que é passada em SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

  2. onCreate()O método é criar as tabelas que você definiu e executar qualquer outro código que você escreveu. No entanto, esse método será chamado apenas se o arquivo SQLite estiver ausente no diretório de dados do seu aplicativo ( /data/data/your.apps.classpath/databases).

  3. Este método não será chamado se você tiver alterado seu código e reiniciado no emulador. Se você deseja onCreate()executar, use adb para excluir o arquivo de banco de dados SQLite.

onUpgrade()

  1. SQLiteOpenHelper deve chamar o super construtor.
  2. O onUpgrade()método será chamado apenas quando o número inteiro da versão for maior que a versão atual em execução no aplicativo.
  3. Se você quer o onUpgrade() método seja chamado, você precisa incrementar o número da versão no seu código.
jeet parmar
fonte
1
Você poderia elaborar mais sua resposta adicionando um pouco mais de descrição sobre a solução que você fornece?
abarisone
10

Talvez seja tarde demais, mas gostaria de compartilhar minha resposta curta e doce. por favor, verifique resposta para um mesmo problema. Definitivamente irá ajudá-lo. Não há mais especificações profundas.

Se você tem certeza da sintaxe para criar tabela, isso pode acontecer quando você adiciona uma nova coluna à mesma tabela, para isso ...

1) Desinstale do seu dispositivo e execute-o novamente.

OU

2) Configuração -> aplicativo -> ClearData

OU

3) Altere DATABASE_VERSIONsua classe "DatabaseHandler" (se você adicionou uma nova coluna, ela será atualizada automaticamente)

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

OU

4) Altere DATABASE_NAMEsua classe "DatabaseHandler" (eu enfrentei o mesmo problema. Mas obtive êxito ao alterar DATABASE_NAME.)

shital
fonte
Eu tenho meu próprio banco de dados e usando a classe SQLiteAssetHelper. Então, eu criei o banco de dados por script sql antes e o banco de dados foi criado. Ao usar o SQLiteAssetHelper, ele não pôde copiar o banco de dados até desinstalar o aplicativo do emulador ou dispositivo, porque era um banco de dados com a mesma versão.
Behzad
4

Pontos a serem lembrados ao estender SQLiteOpenHelper

  1. super(context, DBName, null, DBversion); - Isso deve ser chamado pela primeira linha do construtor
  2. substituir onCreatee onUpgrade(se necessário)
  3. onCreateserá invocado apenas quando getWritableDatabase()ou getReadableDatabase()for executado. E isso será chamado apenas uma vez quando um DBNameespecificado na primeira etapa não estiver disponível. Você pode adicionar a consulta de criação de tabela no onCreatemétodo
  4. Sempre que você quiser adicionar uma nova tabela, basta alterar DBversione fazer as consultas na onUpgradetabela ou simplesmente desinstalar e instalar o aplicativo.
JibinNajeeb
fonte
3

onCreate é chamado pela primeira vez quando a criação de tabelas é necessária. Precisamos substituir esse método em que escrevemos o script para criação de tabela que é executado pelo SQLiteDatabase. método execSQL. Após executar na implantação inicial, esse método não será chamado em diante.

onUpgrade Esse método é chamado quando a versão do banco de dados é atualizada. Suponha que, pela primeira vez, a versão do banco de dados seja 1 e, na segunda, houve alterações na estrutura do banco de dados, como adicionar coluna extra na tabela. Suponha que a versão do banco de dados seja 2 agora.

Faxriddin Abdullayev
fonte
2

Você pode criar banco de dados e tabela como

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

Nota: se você deseja criar outra tabela ou adicionar colunas ou nenhuma tabela, basta incrementar a VERSION

Enamul Haque
fonte
2

Banco de dados sqlite substitui dois métodos

1) onCreate (): esse método é chamado apenas uma vez quando o aplicativo é iniciado pela primeira vez. Por isso, chamou apenas uma vez

2) onUpgrade () Esse método é chamado quando alteramos a versão do banco de dados e, em seguida, esse método é chamado. Ele é usado para alterar a estrutura da tabela, como adicionar uma nova coluna após criar o DB Schema

sushant suryawanshi
fonte
1

nenhuma tabela encontrada é principalmente quando você não abriu a SQLiteOpenHelperclasse com getwritabledata()e, antes disso, você também deve chamar make constructor com databasename & version. E OnUpgradeé chamado sempre que houver um valor de atualização no número da versão fornecido na SQLiteOpenHelperclasse.

Abaixo está o snippet de código (nenhuma coluna encontrada pode ser por causa da ortografia no nome da coluna):

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

}
Bharat Lalwani
fonte
1

Se você esquecer de fornecer uma string "name" como o segundo argumento para o construtor, ele criará um banco de dados "in-memory" que será apagado quando você fechar o aplicativo.

phreakhead
fonte
0

Desinstale seu aplicativo do emulador ou dispositivo. Execute o aplicativo novamente. (OnCreate () não é executado quando o banco de dados já existe)

Nand gopal
fonte
0

O nome do seu banco de dados deve terminar com .db e as cadeias de consulta devem ter um terminador (;)

Omer Haqqani
fonte
0

Verifique novamente sua consulta na classe DatabaseHandler / DatabaseManager (que você já fez)

venu
fonte
0

No meu caso, recebo itens do arquivo XML com <string-array>, onde armazeno <item>s. Nestes <item>s, mantenho cadeias SQL e aplico um por um databaseBuilder.addMigrations(migration). Eu cometi um erro, esqueci de adicionar\ antes da citação e recebi a exceção:

android.database.sqlite.SQLiteException: não existe essa coluna: some_value (código 1 SQLITE_ERROR):, durante a compilação: INSERT INTO table_name (id, nome) VALUES (1, some_value)

Portanto, esta é uma variante correta:

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

O método do Sqliteopenhelper possui métodos create e upgrade, create é usado quando qualquer tabela é criada pela primeira vez e o método de atualização é chamado sempre que o número de colunas da tabela é alterado.

Vishwa Pratap
fonte
O método onUpgrade é chamado quando a versão do banco de dados aumenta, não quando o número de colunas é alterado. Ref: developer.android.com/reference/android/database/sqlite/... , int, int)
Roger Huang