SQLiteOpenHelper onCreate()/ onUpgrade()何时运行?


293

我已经在自己的表格中创建了表格,SQLiteOpenHelper onCreate()但收到了

SQLiteException: no such table

要么

SQLiteException: no such column

错误。为什么?

注意:

(这是每周数十个类似问题的汇总摘要。尝试在此处提供“规范”社区Wiki问题/答案,以便将所有这些问题都可以作为参考。)


12
@Ndupza这不是我的实际问题,只是厌倦了第N次写相同的答案/评论。
laalto 2014年

Answers:


352

SQLiteOpenHelper onCreate()onUpgrade()当实际打开数据库时会调用和回调,例如通过调用getWritableDatabase()。创建数据库助手对象本身时,不会打开数据库。

SQLiteOpenHelper版本化数据库文件。版本号是int传递给构造函数的参数。在数据库文件中,版本号存储在中PRAGMA user_version

onCreate()仅在数据库文件不存在且刚创建时运行。如果onCreate()成功返回(不引发异常),则假定使用请求的版本号创建数据库。这意味着,您不应该自己SQLException陷入onCreate()

onUpgrade()仅在数据库文件存在但存储的版本号低于构造函数中的请求时调用。本onUpgrade()应更新表架构所需的版本。

在代码(onCreate())中更改表模式时,应确保数据库已更新。两种主要方法:

  1. 删除旧的数据库文件,以便onCreate()再次运行。在您可以控制安装版本并且数据丢失不是问题的开发时间,这通常是首选。删除数据库文件的一些方法:

    • 卸载应用程序。使用应用程序管理器或adb uninstall your.package.name从外壳程序。

    • 清除应用程序数据。使用应用程序管理器。

  2. 增加数据库版本,以便onUpgrade()调用它。随着需要更多代码,这会稍微复杂一些。

    • 对于开发时架构升级而言,数据丢失不是问题,您可以使用execSQL("DROP TABLE IF EXISTS <tablename>")来删除现有表并调用onCreate()以重新创建数据库。

    • 对于已发布的版本,应实施数据迁移,以onUpgrade()使用户不会丢失其数据。


2
@Laalto // onUpgrade()中的数据迁移//您能否对此进行解释。
bCliks 2014年

2
@bala不在此问题/答案的范围内。如果您有问题,请随时将其发布为问题。
laalto 2014年

2
@Jaskey版本号适用于您的代码,即代码期望针对哪个架构版本运行。如果文件较旧(来自应用程序的先前版本),则需要对其进行升级。
laalto 2014年

4
因此,每次修改架构时,我都需要在SQLiteHelper中对DB VERSION进行硬编码,以便在运行旧应用程序并获取db连接并发现它已旧时,然后将重新初始化onUpgrade而不是onCreate,是这样吗?对?
Jaskey 2014年

2
谢谢 !这对我来说很有意义。请验证我是否理解。因此,我们需要执行1.每次更新架构时,都要修改DB_VERSION变量(硬代码)。2.在中onUpdate(),检查每个旧版本并进行适当的数据迁移。然后,当用户更新其应用程序(它们具有旧的db文件)时,onUpgrade将被触发,如果该用户是新安装的,则将onCreate()被触发。
Jaskey 2014年

97

根据Jaskey的要求,在此处进一步添加缺失点

数据库版本存储在SQLite数据库文件中。

catch是构造函数

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

因此,当使用name(第二个参数)调用数据库帮助程序构造函数时,平台会检查数据库是否存在以及数据库是否存在,它将从数据库文件头获取版本信息并触发正确的回调

如较早的答案中已经解释的,如果名称不存在的数据库将触发onCreate

下面的说明onUpgrade以示例说明情况。

假设您的应用程序的第一个版本具有DatabaseHelper(extended SQLiteOpenHelper),构造函数将version传递为as 1,然后为升级后的应用程序提供了新的源代码,其版本传递为2,然后在DatabaseHelper构建时自动触发平台,并onUpgrade通过查看文件已存在来触发该平台,但版本低于您通过的当前版本。

现在说你是刨去给应用程序与数据库版本的第三个版本3(DB版本会增加,只有当数据库架构修改)。在这种增量式升级中,您必须逐个增量地编写每个版本的升级逻辑,以便获得更好的可维护代码

下面的示例伪代码:

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

注意breakcase 1和中缺少的语句2。这就是我所说的增量升级。

假设旧版本是2,新版本是4,则逻辑将数据库从升级23,然后升级到4

如果旧版本3和新版本4,它只是运行升级的逻辑34


1
我认为您希望将switch(newVersion)改为switch(oldVersion)。您可能还需要验证newVersion是否为4(而不是5或3;因为您的逻辑是假设新版本应为4),如果旧版本为2而新版本为5,您将击中情况4:并从3升级到4(这可能不是预期的行为)。
joe

右-错字..但如果有新的版本是5 - >则总是抛出IllegalStateException异常和开发人员将通过增加情况下5 ..来修复它
小金

1
如果用户仅将其应用程序从版本2升级到版本3,该怎么办?同样在这种情况下,直到情况4的所有情况都将运行。
Paramvir Singh

6
@param用户无法执行此操作。他只能将2升级到最新版本(此处为4)。
Habeeb Perwad 2015年

20

onCreate()

  1. 当我们第一次创建数据库(即数据库不存在)时,请onCreate()创建具有传入版本的数据库 SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)

  2. onCreate()方法是创建您定义的表并执行您编写的任何其他代码。但是,仅当应用程序的数据目录(/data/data/your.apps.classpath/databases)中缺少SQLite文件时,才会调用此方法。

  3. 如果您更改了代码并在模拟器中重新启动,则不会调用此方法。如果要onCreate()运行,则需要使用adb删除SQLite数据库文件。

onUpgrade()

  1. SQLiteOpenHelper 应该调用超级构造函数。
  2. onUpgrade()仅当版本整数大于应用程序中运行的当前版本时,才会调用此方法。
  3. 如果要onUpgrade()调用该方法,则需要在代码中增加版本号。

1
您能否详细说明您的答案,并提供有关您提供的解决方案的更多说明?
abarisone 2015年

10

可能为时已晚,但我想分享我简短而甜美的答案。请检查“ 答案” 是否存在相同问题。肯定会对您有帮助。没有更深的规格。

如果您对创建表的语法有信心,那么在同一表中添加新列时可能会发生这种情况。

1)从设备上卸载并再次运行。

要么

2)设置->应用-> ClearData

要么

3)更改DATABASE_VERSION“ DatabaseHandler”类(如果添加了新列,它将自动升级)

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

要么

4)更改DATABASE_NAME您的“ DatabaseHandler”类(我遇到了同样的问题。但是通过更改我成功了DATABASE_NAME。)


我有自己的数据库,并使用SQLiteAssetHelper类。因此,我确实在使用sql脚本创建数据库之前就创建了数据库。通过使用SQLiteAssetHelper,直到从模拟器或设备上卸载应用程序后,它才能复制数据库,因为它是具有相同版本的数据库。
Behzad

4

扩展时要记住的要点 SQLiteOpenHelper

  1. super(context, DBName, null, DBversion); -应该在构造函数的第一行调用
  2. 覆盖onCreateonUpgrade(如果需要)
  3. onCreate仅在getWritableDatabase()getReadableDatabase()执行时才会调用。并且仅当DBName第一步中的指定不可用时才调用一次。您可以在onCreate方法上添加创建表查询
  4. 每当您想添加新表时,只需更改DBversion并在onUpgrade表中进行查询,或者简单地卸载然后安装该应用程序即可。

3

需要创建表时,首次调用onCreate。我们需要在编写用于创建表的脚本时覆盖此方法,该脚本由SQLiteDatabase执行。execSQL方法。在首次部署中执行后,将不会再调用此方法。

onUpgrade 在升级数据库版本时调用此方法。假设第一次部署的数据库版本为1,第二次部署的数据库结构发生了变化,例如在表中添加了额外的列。假设数据库版本现在为2。


2

您可以像创建数据库和表

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

注意:如果要创建另一个表或添加列或不添加此类表,只需增加VERSION


2

SQLite数据库覆盖两种方法

1)onCreate():此方法仅在应用程序首次启动时被调用一次。所以只叫了一次

2)onUpgrade()当我们更改数据库版本时调用此方法,然后调用此方法。它用于更改表结构,例如在创建数据库模式后添加新列


1

找不到这样的表主要是因为您没有使用来打开SQLiteOpenHelper类,getwritabledata()并且在此之前还必须用databasename&version调用make构造函数。OnUpgrade当在SQLiteOpenHelper类中给定的版本号中有升级值时,就会调用并且。

以下是代码段(未找到此列可能是由于列名中的拼写所致):

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

如果忘记提供“名称”字符串作为构造函数的第二个参数,它将创建一个“内存”数据库,当您关闭应用程序时该数据库将被删除。


0

从仿真器或设备上卸载应用程序。再次运行该应用程序。(当数据库已经存在时,不执行OnCreate())



0

在您的DatabaseHandler / DatabaseManager类中重新检查您的查询(无论您采取哪种方法)


0

在我的情况下,我从XML文件中获取<string-array>,其中存储<item>。在这些代码中,<item>我持有SQL字符串,并与一对一地应用databaseBuilder.addMigrations(migration)。我犯了一个错误,忘了\在引号前添加并得到了异常:

android.database.sqlite.SQLiteException:无此类列:some_value(代码1 SQLITE_ERROR):,而在编译时:INSERT INTO table_name(id,name)VALUES(1,some_value)

因此,这是一个正确的变体:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.