【问题标题】: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 次写相同的答案/评论。

标签: android sqlite android-sqlite sqlexception sqliteopenhelper


【解决方案1】:

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

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

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

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

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

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

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

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

  2. 增加数据库版本以便调用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()

  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() 方法,则需要在代码中增加版本号。

【讨论】:

  • 能否请您详细说明您的答案,添加更多关于您提供的解决方案的描述?
【解决方案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时要记住的几点

  1. super(context, DBName, null, DBversion); - 这应该在构造函数的第一行调用
  2. 覆盖 onCreateonUpgrade(如果需要)
  3. onCreate 只会在 getWritableDatabase()getReadableDatabase() 被执行时被调用。这只会在第一步中指定的DBName 不可用时调用一次。您可以在onCreate 方法上添加创建表查询
  4. 当您想添加新表时,只需更改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 方法。

                      【讨论】:

                      猜你喜欢
                      相关资源
                      最近更新 更多