Flutter查询预填充SQLite数据库提示no such table找不到表如何解决
问题产生原因
- 最核心的问题是你的代码把资源加载、复制阶段的异常全静默吞了,只要
rootBundle.load或者文件写入阶段报错,代码不会中断,会直接往下执行openDatabase。这时候目标路径下没有正确的数据库文件,sqflite会自动创建一个空的同名数据库,查任何表都会报表不存在。 - 大概率你是没在
pubspec.yaml里正确注册assets路径下的ANUARIO.db,或者注册完没执行flutter clean就直接热重载/热重启,assets资源根本没被打包进安装包,rootBundle.load直接抛错被你catch住只打了个log,没做任何处理。 - 如果你之前调试的时候已经运行过一次应用,当时复制失败生成了0字节/损坏的空db文件在应用文档目录,后续启动时
dbExists判断会认为文件已存在,跳过复制逻辑,一直打开这个损坏的空库,永远报错。 - 小概率是你assets里的源数据库文件本身就没建
empresa表,或者表名大小写、拼写不对,再或者Android平台WAL日志模式兼容问题导致数据库读取异常。
修复步骤
- 先检查
pubspec.yaml的assets配置,确保数据库路径注册正确,配置完成后必须执行flutter clean再全量重新构建安装,热重载不会同步新加入的assets资源,正确配置示例:
flutter: assets: - assets/ANUARIO.db # 如果你把db放在assets的子目录比如assets/db/下,这里路径要对应改对
- 删掉原来静默吞异常的逻辑,资源加载、复制阶段报错直接抛出,方便定位问题,不要打印完错误就继续执行后续打开数据库的逻辑。
- 先卸载手机/模拟器上的现有应用,清空之前生成的损坏缓存文件,再重新安装运行,避免旧的坏库一直被读取。
- 给数据库复制逻辑加完整性校验,比如对比复制后文件和assets源文件的大小,不一致就删掉旧文件重新复制,后续要更新内置数据库的时候也可以通过版本标记判断是否需要覆盖复制。
- 如果以上步骤做完还是报错,打开数据库后执行
PRAGMA journal_mode = DELETE关闭WAL模式,解决Android平台上预填充数据库的WAL文件兼容问题。
修正后的完整代码
import 'dart:io'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter/services.dart'; import 'package:sqflite/sqflite.dart'; class DBHelper { static Database? _db; Future<Database> get db async => _db ??= await initDB(); Future<Database> initDB() async { Directory documentDirectory = await getApplicationDocumentsDirectory(); String path = join(documentDirectory.path, "ANUARIO.db"); File dbFile = File(path); bool needCopy = !await dbFile.exists(); if (!needCopy) { // 校验现有文件和源文件大小是否一致,不一致说明是损坏的旧文件,重新复制 ByteData sourceInfo = await rootBundle.load(join("assets", "ANUARIO.db")); if (await dbFile.length() != sourceInfo.lengthInBytes) { await dbFile.delete(); needCopy = true; } } if (needCopy) { ByteData data = await rootBundle.load(join("assets", "ANUARIO.db")); List<int> bytes = data.buffer.asUint8List( data.offsetInBytes, data.lengthInBytes ); // flush: true 确保数据完全写入磁盘后再往下执行 await dbFile.writeAsBytes(bytes, flush: true); } Database theDB = await openDatabase(path); // 可选:解决Android平台WAL兼容问题 await theDB.execute("PRAGMA journal_mode = DELETE"); // 调试阶段可以放开下面注释,打印当前库所有表确认复制成功 // List<Map> tables = await theDB.rawQuery("SELECT name FROM sqlite_master WHERE type='table'"); // debugPrint("当前库表列表:$tables"); return theDB; } }
内容的提问来源于stack exchange,提问作者joegsuero
相关产品推荐
相关产品推荐

