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】:SQLiteOpenHelper
onCreate()
和onUpgrade()
回调在数据库实际打开时调用,例如通过调用getWritableDatabase()
。创建数据库助手对象本身时,不会打开数据库。
SQLiteOpenHelper
版本数据库文件。版本号是传递给constructor 的int
参数。在数据库文件中,版本号存储在PRAGMA user_version
中。
onCreate()
仅在数据库文件不存在且刚刚创建时运行。如果onCreate()
成功返回(不抛出异常),则假定使用请求的版本号创建数据库。言下之意,您不应该自己在onCreate()
中捕获SQLException
s。
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);
注意1
和2
中缺少的break
语句。这就是我所说的增量升级。
如果旧版本是2
,新版本是4
,那么逻辑会将数据库从2
升级到3
再升级到4
如果旧版本是3
,新版本是4
,它只会运行3
到4
的升级逻辑
【讨论】:
我认为您希望您的 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);
- 这应该在构造函数的第一行调用
覆盖 onCreate
和 onUpgrade
(如果需要)
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创建多个表,调试时找不到表名?