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

When does SQLiteOpenHelper onCreate() / onUpgrade() run?

提问人: 提问时间:2/19/2014 最后编辑:9 revs, 6 users 43%laalto 更新时间:2/2/2021 访问量:136886

问:

我已经在我的 但是 接收SQLiteOpenHelperonCreate()

SQLiteException: no such table

SQLiteException: no such column

错误。为什么?

注意:

(这是每周数十个类似问题的合并摘要。试图在这里提供一个“规范的”社区维基问题/答案,以便所有这些问题都可以被引导到一个好的参考。

android sqlite android-sqlite sqlexception sqliteopenhelper

评论

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

答:

374赞 2 revs, 2 users 95%laalto #1

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

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

onCreate()仅当数据库文件不存在且刚刚创建时才运行。如果返回成功(未引发异常),则假定使用请求的版本号创建数据库。言下之意,你不应该在自己身上抓住s。onCreate()SQLExceptiononCreate()

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

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

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

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

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

  2. 递增数据库版本,以便调用该版本。由于需要更多代码,这稍微复杂一些。onUpgrade()

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

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

评论

2赞 bCliks 9/14/2014
@Laalto //onUpgrade() 中的数据迁移 // 你能解释一下吗?
2赞 laalto 9/14/2014
@bala 不在本问答范围内。如果您有任何问题,请随时将其作为问题发布。
2赞 laalto 10/13/2014
@Jaskey 版本号是针对代码的,即代码预期运行的架构版本。如果文件较旧(来自应用的先前版本),则需要对其进行升级。
4赞 JaskeyLam 10/13/2014
所以,我每次修改schema时都需要在SQLiteHelper中对DB VERSION进行硬编码,这样当旧的应用程序运行并获取db连接并发现它是旧的时,onUpgrade将被trgiigered而不是onCreate,这是对的吗?
2赞 JaskeyLam 10/13/2014
谢谢!这对我来说很有意义。请验证我是否理解。所以我们需要做 1.每次更新模式时,修改DB_VERSION变量(硬代码)。2.在,检查每个旧版本并进行适当的数据迁移。然后,当用户更新他们的应用程序(他们有旧的数据库文件)时,将被触发,如果用户是新安装的,则被触发。onUpdate()onUpgradeonCreate()
105赞 7 revs, 6 users 67%Aun #2

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

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

catch 是构造函数

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

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

正如在较早的答案中已经解释的那样,如果具有该名称的数据库不存在,它将触发 .onCreate

下面的解释用一个例子来解释情况。onUpgrade

比如说,您的第一个版本的应用程序具有(扩展)构造函数,将版本传递为,然后您提供了一个升级的应用程序,其中包含将版本传递为,然后在构造时自动触发,平台通过看到文件已经存在来触发,但版本低于您传递的当前版本。DatabaseHelperSQLiteOpenHelper12DatabaseHelperonUpgrade

现在,假设您计划提供第三个版本的应用程序,其 db 版本为 (db version 仅在要修改数据库架构时增加)。在此类增量升级中,必须以增量方式编写每个版本的升级逻辑,以获得更好的可维护代码3

下面的伪代码示例:

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

请注意 case 和 中缺少的语句。这就是我所说的增量升级。break12

假设旧版本是,新版本是,那么逻辑会将数据库从 到 ,然后24234

如果旧版本是,而新版本是 ,则只会运行 的升级逻辑3434

评论

2赞 joe p 11/15/2014
我认为您希望您的 switch(newVersion) 改为 switch(oldVersion)。您可能还想验证 newVersion 是否为 4(而不是 5 或 3;因为您的逻辑假定新版本应为 4)。事实上,如果旧版本是 2,新版本是 5,您将遇到案例 4:并从 3 升级到 4(这可能不是预期的行为)。
0赞 Aun 11/15/2014
对 - 错别字..但是如果新版本是 5 ->那么它将始终抛出 IllegalStateException,开发人员将通过添加案例 5 来修复它。
1赞 Paramvir Singh 2/5/2015
如果用户仅将其应用从版本 2 升级到版本 3,该怎么办?在这种情况下,案例 4 之前的所有案例都将运行。
7赞 Mohammed H 2/19/2015
@param用户都无法做到这一点。他只能将 2 升级到最新版本(这里是 4)。
19赞 3 revs, 2 users 67%jeet parmar #3

onCreate()

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

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

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

onUpgrade()

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

评论

1赞 abarisone 6/16/2015
您能否详细说明您的答案,并添加有关您提供的解决方案的更多描述?
1赞 Nand gopal #4

从模拟器或设备中卸载应用程序。再次运行应用。(当数据库已存在时,不执行 OnCreate())

5赞 2 revs, 2 users 57%JibinNajeeb #5

扩展时要记住的要点SQLiteOpenHelper

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

Sqliteopenhelper的方法有create和upgrade方法,create在首次创建任何表时使用,每次表的列数发生变化时都会调用upgrade方法。

评论

0赞 Roger Huang 4/26/2017
onUpgrade 方法在数据库版本增加时调用,而不是在列数更改时调用。参考: developer.android.com/reference/android/database/sqlite/..., int, int)
9赞 5 revs, 3 users 54%shital #7

也许我为时已晚,但我想分享我简短而甜蜜的答案。 请检查“回答”以解决相同的问题。它肯定会对你有所帮助。没有更深层次的规格。

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

1)从您的设备中卸载并再次运行。

2) 设置 -> 应用程序 -> ClearData

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

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

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

评论

0赞 Behzad 10/31/2017
我有自己的数据库并使用SQLiteAssetHelper类。所以,我之前确实通过sql脚本创建了数据库,并且创建了数据库。通过使用 SQLiteAssetHelper,在从模拟器或设备中卸载应用之前,它无法复制数据库,因为它是具有相同版本的数据库。
1赞 2 revs, 2 users 97%Bharat Lalwani #8

没有找到这样的表主要是当你没有打开类时,在此之前,你还必须用databasename和version调用make构造函数。 每当类中给定的版本号中有升级值时,都会调用。SQLiteOpenHelpergetwritabledata()OnUpgradeSQLiteOpenHelper

下面是代码片段(未找到此类列可能是因为列名称中的拼写):

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

}
3赞 Enamul Haque #9

您可以创建数据库和表,例如

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

0赞 Omer Haqqani #10

数据库名称必须以 .db 结尾,查询字符串也必须具有终止符 (;)

1赞 phreakhead #11

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

3赞 Fakhriddin Abdullaev #12

onCreate 在需要创建表时首次调用。我们需要重写这个方法,其中我们编写了由 SQLiteDatabase 执行的表创建脚本。execSQL 方法。首次部署执行后,将不再调用此方法。

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

0赞 venu #13

在 DatabaseHandler/DatabaseManager 类中重新检查您的查询(无论您采用哪个类)

2赞 rawhost #14

Sqlite 数据库覆盖两种方法

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

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

0赞 CoolMind #15

就我而言,我从 XML 文件中获取项目,其中我存储了 s。在这些 s 中,我保存 SQL 字符串并用 .我犯了一个错误,忘记在引用之前添加并得到例外:<string-array><item><item>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>