SQLiteOpenHelper onCreate() / onUpgrade() 啥时候运行?

Posted

技术标签:

【中文标题】SQLiteOpenHelper onCreate() / onUpgrade() 啥时候运行?【英文标题】:When does SQLiteOpenHelper onCreate() / onUpgrade() run?SQLiteOpenHelper onCreate() / onUpgrade() 什么时候运行? 【发布时间】:2016-09-17 18:09:05 【问题描述】:

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

SQLiteException: no such table

SQLiteException: no such column

错误。为什么?

注意:

(这是每周数十个类似问题的合并摘要。尝试在此处提供“规范”社区 wiki 问题/答案,以便将所有这些问题指向一个好的参考。)

【问题讨论】:

@Ndupza 这不是我的实际问题,只是受够了第 N 次写相同的答案/评论。 【参考方案1】:

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

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

onCreate() 仅在数据库文件不存在且刚刚创建时运行。如果onCreate() 成功返回(不抛出异常),则假定使用请求的版本号创建数据库。言下之意,您不应该自己在onCreate() 中捕获SQLExceptions。

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

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

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

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

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

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

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

    对于已发布的版本,您应该在onUpgrade() 中实施数据迁移,以免您的用户丢失数据。

【讨论】:

@Laalto //onUpgrade()中的数据迁移//你能解释一下吗? @bala 不在此问题/答案的范围内。如果您有任何问题,请随时将其作为问题发布。 @Jaskey 版本号适用于您的代码,即代码期望运行的架构版本。如果文件较旧(来自您的应用的早期版本),则需要升级。 所以,我每次修改架构时都需要在 SQLiteHelper 中硬编码 DB VERSION,这样当旧应用运行并获取 db 连接并发现它是旧的时,然后 onUpgrade 将被触发而不是 onCreate,对吗? 谢谢!这对我来说很有意义。请验证我是否理解得很好。所以我们需要做 1. 每次更新架构时,修改 DB_VERSION 变量(硬代码)。 2.在onUpdate(),检查每个旧版本并进行适当的数据迁移。然后当用户更新他们的应用程序时(他们有旧的数据库文件),onUpgrade 将被触发,如果用户是新安装的,onCreate() 将被触发。【参考方案2】:

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

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

catch 是构造函数

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

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

正如旧答案中已经解释的那样,如果同名的数据库不存在,则会触发onCreate

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

假设你的第一个版本的应用程序有DatabaseHelper(扩展SQLiteOpenHelper),构造函数传递版本为1,然后你提供了一个升级后的应用程序,新源代码的版本传递为2,然后在DatabaseHelper构建时自动触发onUpgrade,看到文件已经存在,但版本低于你通过的当前版本。

现在假设您计划提供第三个版本的应用程序,其 db 版本为 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);
  

注意12 中缺少的break 语句。这就是我所说的增量升级。

如果旧版本是2,新版本是4,那么逻辑会将数据库从2升级到3再升级到4

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

【讨论】:

我认为您希望您的 switch(newVersion) 改为 switch(oldVersion)。您可能还想验证 newVersion 是否为 4(而不是 5 或 3;因为您的逻辑假设新版本应为 4)。事实上,如果旧版本为 2,新版本为 5,您将击中 case 4: 并从 3 升级到 4(这可能不是预期的行为)。 对 - 错字.. 但如果新版本是 5 -> 那么它总是会抛出 IllegalStateException 并且开发人员将通过添加案例 5 来修复它.. 如果用户只将他的应用程序从版本 2 升级到 3 怎么办?在这种情况下,直到案例 4 的所有案例都将运行。 @param 用户不能这样做。他只能将 2 升级到最新(这里是 4)。【参考方案3】:

onCreate()

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

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

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

onUpgrade()

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

【讨论】:

能否请您详细说明您的答案,添加更多关于您提供的解决方案的描述?【参考方案4】:

也许我来得太晚了,但我想分享我简短而甜蜜的答案。 请检查Answer 是否有相同的问题。它肯定会帮助你。没有更深入的规范。

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

1) 从您的设备上卸载并重新运行。

2) 设置 -> 应用 -> ClearData

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

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

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

【讨论】:

我有自己的数据库并使用 SQLiteAssetHelper 类。所以,我之前确实通过 sql 脚本创建了数据库,并且创建了数据库。通过使用 SQLiteAssetHelper,它无法复制数据库,直到从模拟器或设备中卸载应用程序,因为它是具有相同版本的数据库。【参考方案5】:

扩展SQLiteOpenHelper时要记住的几点

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

【讨论】:

【参考方案6】:

你可以像这样创建数据库和表

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

【讨论】:

【参考方案7】:

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

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

【讨论】:

【参考方案8】:

Sqlite 数据库重写两个方法

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

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

【讨论】:

【参考方案9】:

没有找到这样的表主要是当你没有用getwritabledata()打开SQLiteOpenHelper类,在此之前你还必须用数据库名和版本调用make构造函数。 只要SQLiteOpenHelper 类中给出的版本号中有升级值,就会调用OnUpgrade

下面是代码sn-p(没有找到这样的列可能是因为列名拼写):

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


【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

您的数据库名称必须以 .db 结尾,而且您的查询字符串必须有一个终止符 (;)

【讨论】:

【参考方案13】:

在你的 DatabaseHandler/DatabaseManager 类中重新检查你的查询(你曾经使用过)

【讨论】:

【参考方案14】:

在我的例子中,我从带有 <string-array> 的 XML 文件中获取项目,我在其中存储 <item>s。在这些<item>s 中,我持有SQL 字符串并与databaseBuilder.addMigrations(migration) 逐一应用。我犯了一个错误,忘记在引用前添加\ 并得到了异常:

android.database.sqlite.SQLiteException: no such column: some_value (code 1 SQLITE_ERROR): , while compile: INSERT INTO table_name(id, name) VALUES(1, some_value)

所以,这是一个正确的变体:

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

【讨论】:

【参考方案15】:

Sqliteopenhelper 的方法有 create 和 upgrade 方法,第一次创建表时使用 create 方法,每次更改表的列数时都会调用 upgrade 方法。

【讨论】:

onUpgrade 方法是在数据库版本增加时调用,而不是在列数更改时调用。参考:developer.android.com/reference/android/database/sqlite/…, int, int)

以上是关于SQLiteOpenHelper onCreate() / onUpgrade() 啥时候运行?的主要内容,如果未能解决你的问题,请参考以下文章

02_SQliteOpenHelper介绍&oncreate方法介绍

SQLiteOpenHelper onCreate() / onUpgrade() 啥时候运行?

SQLiteOpenHelper onCreate() / onUpgrade() 啥时候运行?

Android问题:关于SQLiteOpenHelper中的onCreate创建多个表,调试时找不到表名?

浅谈SQLiteOpenHelper之onUpgrade例子

Android笔记——SQLiteOpenHelper类