Flutter中使用sqflite插入HTTP Get获取的数据时无法存储全部记录
问题分析与解决方案
你遇到的核心问题是异步操作未被正确等待,导致数据库插入操作在未全部完成时就被中断了——这也是为什么HTTP能拿到149条数据,但只有101条插入成功的原因。
问题根源拆解
看你ApiProvider里的这段代码:
(res as List).map((product) { DatabaseHelper.db.insertProduct(NewProduct.fromJson(product)); }).toList();
这里的insertProduct是异步方法(返回Future),但你只是在map里调用它,却没有等待这些Future执行完毕。map只会生成一堆未被处理的Future,而你的getPro方法会直接返回,不会等所有插入完成。
当main函数里的await apiProvider.getAllProducts()执行完时,实际上大部分插入操作还在后台跑,而此时应用开始启动runApp,事件循环优先级转向UI渲染,部分未完成的插入操作就被中断了,所以只成功插入了最先完成的101条。
修复方案
方案1:等待所有单个插入完成
修改ApiProvider的getPro方法,用Future.wait等待所有插入操作完成:
class ApiProvider { Future<List<NewProduct>?> getPro() async { Response response = await Dio().get(Info.productUrl); var res = response.data; print("PRODUCT LENGTH-" + res.length.toString()); // 用Future.wait等待所有插入Future完成 await Future.wait((res as List).map((product) async { await DatabaseHelper.db.insertProduct(NewProduct.fromJson(product)); })); // 返回转换后的产品列表(可选) return (res as List).map((product) => NewProduct.fromJson(product)).toList(); } }
同时,main函数里的deleteAllProduct也是异步操作,必须加await,否则可能删除还没完成就开始插入:
void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); FirebaseMessaging.onBackgroundMessage(backgroundHandler); DatabaseHelper.db.database; if (await InternetConnectionChecker().hasConnection) { print("INTERNERT AVAILABLE"); // 等待删除操作完成 await DatabaseHelper.db.deleteAllProduct(); await apiProvider.getAllProducts(); // 这里的延迟可以去掉了,因为已经等待所有插入完成 runApp(MyApp()); } else { print("NO INTERNET"); runApp(NoConn()); } }
方案2:改用批量插入(更高效)
逐个插入149条数据效率较低,推荐用SQLite的批量操作来优化,同时避免异步等待的问题:
首先在DatabaseHelper里新增批量插入方法:
// 新增批量插入方法 Future<void> insertProducts(List<NewProduct> products) async { final db = await database; final batch = db.batch(); for (var product in products) { batch.insert('SA_PRO_MASTER', product.toJson()); } // 提交批量操作,noResult设为true可以忽略返回结果提升性能 await batch.commit(noResult: true); }
然后修改ApiProvider的getPro方法:
class ApiProvider { Future<List<NewProduct>?> getPro() async { Response response = await Dio().get(Info.productUrl); var res = response.data; print("PRODUCT LENGTH-" + res.length.toString()); // 先把所有JSON转成NewProduct对象 final productList = (res as List).map((product) => NewProduct.fromJson(product)).toList(); // 调用批量插入 await DatabaseHelper.db.insertProducts(productList); return productList; } }
额外排查建议
为了避免无声失败,建议给数据库操作加上错误捕获:
// 修改单个插入方法 insertProduct(NewProduct listPro) async { final db = await database; try { final res = await db.insert('SA_PRO_MASTER', listPro.toJson()); print("INSERT RES - " + res.toString()); return res; } catch(e) { print("插入产品失败: ${listPro.sa_proid} - $e"); rethrow; // 可以选择抛出错误或者处理 } }
这样如果有某个产品因为数据问题(比如NOT NULL字段为空)插入失败,你能在控制台看到具体错误。
内容的提问来源于stack exchange,提问作者Savan Nagda
相关产品推荐
相关产品推荐

