在 database/storage dropping/deleting table 之后未调用 Sqlite onCreate

Sqlite onCreate isn't invoked after dropping/deleting the table from the database/storage

最小可重现代码:

class Helper {
  Database _db;

  Future<Database> initDb() async {
    if (_db == null) {
      final directory = await getApplicationDocumentsDirectory();
      _db = await openDatabase(join(directory.path, 'foo.db'), version: 1, onCreate: _onCreate);
    }
    return _db;
  }

  Future<void> _onCreate(Database db, _) async {
    print('onCreate');
    await db.transaction((txn) async {
      await txn.execute('CREATE TABLE tableName(abc TEXT)');
    });
  }
}

这是我的主要方法:

void main() async {
  final directory = await getApplicationDocumentsDirectory();
  final file = File(join(directory.path, 'foo.db'));
  if (await file.exists()) {
    await file.delete();
  }

  // If there had been a file, it's deleted now.
  final helper = Helper();
  await helper.initDb(); // This must fire `onCreate` but it doesn't.
}

每次 运行 main 方法时,它应该执行 Helper class 中的 onCreate 方法,但它只执行一次。我在这里做错了什么?

这是默认行为,因为创建数据库时会执行 onCreate 方法。由于数据库是在没有再次执行之前创建的。

如果您打算清除 table 的行,请使用 TRUNCATE 命令。有关更多信息,请查看 link 比较 drop table 和 truncate table here

onCreate只有在没有数据库文件时才会执行。不是当数据库中没有 table 时。如果您删除数据库文件,那么将在控制台中打印一条消息 onCreate。这是一个例子:

void main() async {
  final directory = await getApplicationDocumentsDirectory();
  final path = join(directory.path, 'foo.db');
  await File(path).delete();

  final helper = Helper();
  await helper.initDb();
  // await helper.dropTable();
}

此代码每隔 运行 程序打印一条日志消息。

这不是 Sqflite 的工作方式。

第一次创建数据库时,您会为其指定一个版本号。在你的情况下,版本是 1.

Sqflite 将检查设备上是否存在数据库如果存在,它会检查设备上数据库的版本号和代码的版本号,如果版本不相同,如果新版本大于旧版本,它将调用 onUpgrade数据库版本。如果旧版本大于新版本,则降级。不会调用 OnCreate。 onCreate 仅在用户第一次安装您的应用程序时被调用。即使您之后放弃 tables。如果您将来需要在生产应用程序上更新数据库,则必须编写 onUpgrade 方法,在该方法中您必须显式删除 tables 并自己调用 onCreate。并升级数据库版本。

见以下代码。每当您需要 dropAll tables 和 createAgain.

时,使用此代码设置并将版本更改为更高的版本号

如果您不想使用这些版本并明确删除 tables 并重新创建它们。删除 table.

后,您必须自己调用 _onCreate
class Helper {
  Database _db;

  Future<Database> initDb() async {
    if (_db == null) {
      final directory = await getDatabasesPath();
      final path = join(directory, 'foo.db');
      _db = await openDatabase(
        path,
        version: 2, //+1 to this number whenever you want to update 
        onCreate: _onCreate,
        onUpgrade: _onUpgrade,
      );
    }
    return _db;
  }

  Future<void> _onUpgrade(
      Database db, int previousVersion, int newVersion) async {
    _db = db;
    await dropTable();
    await _onCreate(db, newVersion);
  }

  Future<void> _onCreate(Database db, int version) async {
    print('onCreate');
    await db.transaction((txn) async {
      await txn.execute('CREATE TABLE tableName(abc TEXT)');
    });
  }

  Future<void> dropTable() async {
    await _db.transaction((txn) async {
      await txn.execute('DROP TABLE IF EXISTS tableName');
    });
  }

问题描述从一开始就变了,很难在评论中做出正确的解释,所以这里又是一个答案。

所有现有的响应仍然有效,但问题现在正在转移到类似 删除数据库后未调用 onCreate 的问题

Every time you run the main method, it should execute the onCreate method in Helper class but it only does that once. What am I doing wrong here?

您并没有具体说明如何 运行(即您之前是否停止过应用程序),所以我假设您只是按下 [=35= 上的 运行 按钮] 执行热重启。

虽然您可能倾向于简单地删除文件,但是您应该使用 deleteDatabase 正确删除数据库。

// Do not call File.delete, it will not work in a hot restart scenario
await File(path).delete();

// Instead do
await deleteDatabase(path);
  • 它将正确关闭任何现有的数据库连接
  • 它将正确处理将 SQLite 置于 奇怪的状态(基本上 'dart' 一方认为数据库已关闭,而 数据库实际上是在本机端打开的)

如果您调用 File.delete,虽然您可能认为它有效(即文件不 存在),因为在热重启场景中数据库可能仍处于打开状态 下一次打开将重新使用打开的连接,并且在某个时候会被写入 使用旧数据,下次打开时不会调用 onCreate 数据库。