|
|
@@ -15,10 +15,15 @@ class SqliteManager {
|
|
|
|
|
|
// 核心配置
|
|
|
String? _dbName;
|
|
|
+ String _dbPath = '';
|
|
|
int _dbVersion = 1;
|
|
|
final List<TableModel> _tableConfigs = [];
|
|
|
Database? _database;
|
|
|
|
|
|
+ // 延时关闭相关变量
|
|
|
+ Timer? _closeTimer;
|
|
|
+ int _pendingRequests = 0;
|
|
|
+
|
|
|
/// 初始化数据库(仅需传入数据库名和表配置)
|
|
|
/// [dbName] 数据库名称
|
|
|
/// [version] 数据库版本(默认1)
|
|
|
@@ -30,26 +35,23 @@ class SqliteManager {
|
|
|
}
|
|
|
_tableConfigs.addAll(TableConfig.defaultConfigs());
|
|
|
logger.d('数据库配置初始化完成: $dbName (v$version)');
|
|
|
+ _dbPath = await _getDbPath();
|
|
|
+ await _getDatabase();
|
|
|
}
|
|
|
|
|
|
- /// 内部方法:获取数据库连接(自动创建/升级)
|
|
|
- Future<Database> _getDatabase() async {
|
|
|
+ Future<String> _getDbPath() async {
|
|
|
if (_dbName == null) {
|
|
|
throw Exception('请先调用 init 方法初始化数据库配置');
|
|
|
}
|
|
|
-
|
|
|
- // 如果已有打开的连接,直接返回
|
|
|
- if (_database != null && _database!.isOpen) {
|
|
|
- return _database!;
|
|
|
- }
|
|
|
-
|
|
|
// 获取数据库路径
|
|
|
// Directory documentsDir = await getApplicationDocumentsDirectory();
|
|
|
// String dbPath = join(documentsDir.path, _dbName!);
|
|
|
|
|
|
// 1. 获取应用文档根目录
|
|
|
- Directory documentsDir = await getApplicationDocumentsDirectory();
|
|
|
- logger.d('应用文档根目录:${documentsDir.path}');
|
|
|
+ Directory? documentsDir = await getExternalStorageDirectory();
|
|
|
+ // logger.d('应用文档根目录1:${documentsDir?.path}');
|
|
|
+ documentsDir ??= await getApplicationDocumentsDirectory();
|
|
|
+ // logger.d('应用文档根目录2:${documentsDir.path}');
|
|
|
// 2. 拼接自定义的 db 子文件夹路径
|
|
|
String dbFolderPath = join(documentsDir.path, 'db');
|
|
|
// 3. 检查并创建 db 文件夹(不存在则创建)
|
|
|
@@ -62,10 +64,22 @@ class SqliteManager {
|
|
|
// 4. 拼接最终的数据库文件路径(db文件夹 + 数据库名)
|
|
|
String dbPath = join(dbFolderPath, _dbName!);
|
|
|
logger.d('数据库文件路径:$dbPath');
|
|
|
+ return dbPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 内部方法:获取数据库连接(自动创建/升级)
|
|
|
+ Future<Database> _getDatabase() async {
|
|
|
+ if (_dbPath.isEmpty) {
|
|
|
+ throw Exception('请先调用 _getDbPath 获取数据库路径');
|
|
|
+ }
|
|
|
+ // 如果已有打开的连接,直接返回
|
|
|
+ if (_database != null && _database!.isOpen) {
|
|
|
+ return _database!;
|
|
|
+ }
|
|
|
|
|
|
// 打开/创建数据库
|
|
|
_database = await openDatabase(
|
|
|
- dbPath,
|
|
|
+ _dbPath,
|
|
|
version: _dbVersion,
|
|
|
onCreate: (db, version) async {
|
|
|
// 批量创建表
|
|
|
@@ -86,7 +100,10 @@ class SqliteManager {
|
|
|
}
|
|
|
},
|
|
|
);
|
|
|
-
|
|
|
+ if (_database == null) {
|
|
|
+ throw Exception('数据库初始化失败');
|
|
|
+ }
|
|
|
+ logger.d('数据库连接成功');
|
|
|
return _database!;
|
|
|
}
|
|
|
|
|
|
@@ -94,22 +111,33 @@ class SqliteManager {
|
|
|
Future<T> _executeWithAutoClose<T>(
|
|
|
Future<T> Function(Database db) action,
|
|
|
) async {
|
|
|
- // 获取连接
|
|
|
+ // 取消之前的关闭定时器(如果有)
|
|
|
+ _closeTimer?.cancel();
|
|
|
+ _pendingRequests++;
|
|
|
final db = await _getDatabase();
|
|
|
-
|
|
|
try {
|
|
|
- // 执行核心操作
|
|
|
final result = await action(db);
|
|
|
return result;
|
|
|
} finally {
|
|
|
- // 操作完成后自动关闭连接
|
|
|
- if (db.isOpen) {
|
|
|
- await db.close();
|
|
|
- _database = null;
|
|
|
+ _pendingRequests--;
|
|
|
+ if (_pendingRequests <= 0) {
|
|
|
+ _scheduleCloseDatabase();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// 安排延时关闭数据库
|
|
|
+ void _scheduleCloseDatabase() {
|
|
|
+ _closeTimer?.cancel();
|
|
|
+ _closeTimer = Timer(Duration(seconds: 5), () {
|
|
|
+ if (_database != null && _database!.isOpen) {
|
|
|
+ _database!.close();
|
|
|
+ _database = null;
|
|
|
+ logger.d('数据库连接已延时关闭');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
// ===================== 通用CRUD方法(自动管理连接) =====================
|
|
|
|
|
|
/// 插入数据
|
|
|
@@ -118,10 +146,20 @@ class SqliteManager {
|
|
|
required Map<String, dynamic> data,
|
|
|
ConflictAlgorithm conflictAlgorithm = ConflictAlgorithm.replace,
|
|
|
}) async {
|
|
|
- data["create_time"] = DateTimeUtil.format(DateTime.now());
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- return await db.insert(table, data, conflictAlgorithm: conflictAlgorithm);
|
|
|
- });
|
|
|
+ try {
|
|
|
+ data["create_time"] = DateTimeUtil.format(DateTime.now());
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ return await db.insert(
|
|
|
+ table,
|
|
|
+ data,
|
|
|
+ conflictAlgorithm: conflictAlgorithm,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e("插入数据失败:表[$table] 数据[$data]", e, s);
|
|
|
+ logger.e("插入数据失败:表[$table] 数据[$data] 错误: $e\n堆栈跟踪: $s");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 批量插入数据
|
|
|
@@ -129,14 +167,18 @@ class SqliteManager {
|
|
|
required String table,
|
|
|
required List<Map<String, dynamic>> dataList,
|
|
|
}) async {
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- final batch = db.batch();
|
|
|
- for (var data in dataList) {
|
|
|
- data["create_time"] = DateTimeUtil.format(DateTime.now());
|
|
|
- batch.insert(table, data);
|
|
|
- }
|
|
|
- await batch.commit(noResult: true);
|
|
|
- });
|
|
|
+ try {
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ final batch = db.batch();
|
|
|
+ for (var data in dataList) {
|
|
|
+ data["create_time"] = DateTimeUtil.format(DateTime.now());
|
|
|
+ batch.insert(table, data);
|
|
|
+ }
|
|
|
+ await batch.commit(noResult: true);
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e("批量插入数据失败:表[$table] 数据条数[${dataList.length}] 错误: $e\n堆栈跟踪: $s");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 查询数据
|
|
|
@@ -151,19 +193,26 @@ class SqliteManager {
|
|
|
int? limit,
|
|
|
int? offset,
|
|
|
}) async {
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- return await db.query(
|
|
|
- table,
|
|
|
- columns: columns,
|
|
|
- where: where,
|
|
|
- whereArgs: whereArgs,
|
|
|
- groupBy: groupBy,
|
|
|
- having: having,
|
|
|
- orderBy: orderBy,
|
|
|
- limit: limit,
|
|
|
- offset: offset,
|
|
|
+ try {
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ return await db.query(
|
|
|
+ table,
|
|
|
+ columns: columns,
|
|
|
+ where: where,
|
|
|
+ whereArgs: whereArgs,
|
|
|
+ groupBy: groupBy,
|
|
|
+ having: having,
|
|
|
+ orderBy: orderBy,
|
|
|
+ limit: limit,
|
|
|
+ offset: offset,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e(
|
|
|
+ "查询数据失败:表[$table] 条件[where:$where, whereArgs:$whereArgs] 错误: $e\n堆栈跟踪: $s",
|
|
|
);
|
|
|
- });
|
|
|
+ return [];
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 更新数据
|
|
|
@@ -173,10 +222,17 @@ class SqliteManager {
|
|
|
String? where,
|
|
|
List<dynamic>? whereArgs,
|
|
|
}) async {
|
|
|
- data["update_time"] = DateTimeUtil.format(DateTime.now());
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- return await db.update(table, data, where: where, whereArgs: whereArgs);
|
|
|
- });
|
|
|
+ try {
|
|
|
+ data["update_time"] = DateTimeUtil.format(DateTime.now());
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ return await db.update(table, data, where: where, whereArgs: whereArgs);
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e(
|
|
|
+ "更新数据失败:表[$table] 数据[$data] 条件[where:$where, whereArgs:$whereArgs] 错误: $e\n堆栈跟踪: $s",
|
|
|
+ );
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 删除数据
|
|
|
@@ -185,27 +241,75 @@ class SqliteManager {
|
|
|
String? where,
|
|
|
List<dynamic>? whereArgs,
|
|
|
}) async {
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- return await db.delete(table, where: where, whereArgs: whereArgs);
|
|
|
- });
|
|
|
+ try {
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ return await db.delete(table, where: where, whereArgs: whereArgs);
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e(
|
|
|
+ "删除数据失败:表[$table] 条件[where:$where, whereArgs:$whereArgs] 错误: $e\n堆栈跟踪: $s",
|
|
|
+ );
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 查询符合条件的数据条数
|
|
|
+ Future<int> queryCount(
|
|
|
+ String table, {
|
|
|
+ String? where,
|
|
|
+ List<dynamic>? whereArgs,
|
|
|
+ }) async {
|
|
|
+ try {
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ final result = await db.query(
|
|
|
+ table,
|
|
|
+ columns: ['COUNT(*) as count'],
|
|
|
+ where: where,
|
|
|
+ whereArgs: whereArgs,
|
|
|
+ );
|
|
|
+
|
|
|
+ if (result.isNotEmpty) {
|
|
|
+ final count = result.first['count'];
|
|
|
+ return count is int ? count : int.parse(count.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e(
|
|
|
+ "查询数据条数失败:表[$table] 条件[where:$where, whereArgs:$whereArgs] 错误: $e\n堆栈跟踪: $s",
|
|
|
+ );
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 执行原生SQL
|
|
|
Future<void> executeSql(String sql, [List<dynamic>? arguments]) async {
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- await db.execute(sql, arguments);
|
|
|
- });
|
|
|
+ try {
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ await db.execute(sql, arguments);
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e("执行SQL失败:SQL[$sql] 参数[$arguments] 错误: $e\n堆栈跟踪: $s");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 执行事务(事务内保持连接,事务结束后关闭)
|
|
|
Future<T> transaction<T>(Future<T> Function(Transaction txn) action) async {
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- return await db.transaction<T>(action);
|
|
|
- });
|
|
|
+ try {
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ return await db.transaction<T>(action);
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e("执行事务失败 错误: $e\n堆栈跟踪: $s");
|
|
|
+ rethrow;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 手动关闭数据库(备用方法)
|
|
|
Future<void> closeDb() async {
|
|
|
+ _closeTimer?.cancel();
|
|
|
+ _pendingRequests = 0;
|
|
|
if (_database != null && _database!.isOpen) {
|
|
|
await _database!.close();
|
|
|
_database = null;
|
|
|
@@ -227,8 +331,13 @@ class SqliteManager {
|
|
|
|
|
|
/// 清空表数据
|
|
|
Future<int> clearTable(String table) async {
|
|
|
- return _executeWithAutoClose((db) async {
|
|
|
- return await db.delete(table);
|
|
|
- });
|
|
|
+ try {
|
|
|
+ return _executeWithAutoClose((db) async {
|
|
|
+ return await db.delete(table);
|
|
|
+ });
|
|
|
+ } on Exception catch (e, s) {
|
|
|
+ logger.e("清空表数据失败:表[$table] 错误: $e\n堆栈跟踪: $s");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
}
|
|
|
}
|