当前位置:   article > 正文

Flutter之数据库的使用(sqflite_common_ffi)_flutter数据库

flutter数据库

sqfliteFlutterSQLite插件,支持的平台有:iOS、Android、MacOS,桌面端可以使用sqflite_common_ffi,本篇文章以sqflite_common_ffi为主。

sqflite_common_ffi定义了一个全局databaseFactoryFfi允许在 Flutter 和 DartVM 上支持 Linux 和 Windows。所以databaseFactory = databaseFactoryFFi就可以带来Linux和Windows的支持。databaseFactory提供了一个直接的 API(openDatabase、deleteDatabase)。

任务要求:构建一个桌面端的任务管理软件,使用sqflite_common_ffi插件。

创建数据库

1、在你的pubspec.yaml文件中添加以下依赖:

2、封装一个数据库操作类 DBHelper()

  1. class DBHelper {
  2. //定义了一个静态变量---_dbHelper,保存DBHelper类的单例实例
  3. static DBHelper? _dbHelper;
  4. //定义了一个静态方法---getInstance()获取DBHelper的单例实例
  5. //如果_dbHelper为空,就创建一个新的DBHelper实例
  6. static DBHelper getInstance() {
  7. if (_dbHelper == null) {
  8. _dbHelper = DBHelper();
  9. }
  10. return _dbHelper!;
  11. }
  12. //_db是一个Database类型的成员,用于存储数据库实例
  13. Database? _db;
  14. //数据库中的表
  15. static final String _ALLTask = "_ALLTask"; //所有任务
  16. //database是一个异步getter,它返回数据库实例。如果_db为空,就调用initDB方法初始化数据库。
  17. Future<Database> get database async {
  18. if (_db != null) {
  19. return _db!;
  20. }
  21. _db = await initDB();
  22. return _db!;
  23. }
  24. }

3、初始化数据库操作  initDB()

a.初始化数据库sqfliteFfiInit();

b.获取databaseFactoryFfi对象

c.使用databaseFactoryFfi 对象来打开数据库,语句:databaseFactory.openDatabase()

String Path.(获取数据库的默认位置,最好使用"path_provider"策略)

path_provider的使用:

1、添加依赖

2、在需要的文件中导入包

import 'package:path/path.dart' as path;

openDatabaseOptions(打开数据库操作)的某些属性:

  1. /// Specify the expected version.
  2. int? version;
  3. /// called right after opening the database.(打开数据库后立即调用)
  4. OnDatabaseConfigureFn? onConfigure;
  5. /// Called when the database is created.(数据库创建时的回调函数)
  6. OnDatabaseCreateFn? onCreate;
  7. /// Called when the database is upgraded.(数据库升级时调用)
  8. OnDatabaseVersionChangeFn? onUpgrade;
  9. /// Called when the database is downgraded.(数据库降级时调用)
  10. ///
  11. /// Use [onDatabaseDowngradeDelete] for re-creating the database
  12. (使用 [onDatabaseDowngradeDelete] 重新创建数据库)
  13. OnDatabaseVersionChangeFn? onDowngrade;
  14. /// Called after all other callbacks have been called.(在调用所有其他回调后调用)
  15. OnDatabaseOpenFn? onOpen;
  16. /// Open the database in read-only mode (no callback called).(以只读模式打开数据库(不调用回调)。)
  17. late bool readOnly;
  18. /// The existing single-instance (hot-restart)(现有单实例(热重启))
  19. late bool singleInstance;

  1. //初始化数据库
  2. initDB()async{
  3. //1、初始化数据库
  4. sqfliteFfiInit();
  5. //2、获取databaseFactoryFfi对象
  6. var databaseFactory = databaseFactoryFfi;
  7. //3、创建数据库
  8. return await databaseFactory.openDatabase(
  9. //数据库路径
  10. path.join(await databaseFactory.getDatabasesPath(), "TO-DO.db"),
  11. //打开数据库操作
  12. options: OpenDatabaseOptions(
  13. //版本
  14. version: 5,
  15. //创建时操作
  16. onCreate: (db,version)async{
  17. print("创建数据库");
  18. return await db.execute(
  19. "CREATE TABLE $_ALLTask ("
  20. "id INTEGER PRIMARY KEY AUTOINCREMENT,"
  21. "content TEXT,"
  22. "ownType STRING,"
  23. "startDate STRING,"
  24. "endDate STRING,"
  25. "createTime STRING,"
  26. "completeTime STRING,"
  27. "repeat STRING,"
  28. "isCompleted INTEGER"
  29. ")"
  30. );
  31. }
  32. )
  33. );
  34. }

至此,数据库创建完成。

数据库操作

增加:

1、Database.insert
  1. Future<int> insert(
  2. String table, //表名
  3. Map<String, Object?> values,//插入的数据
  4. {String? nullColumnHack,
  5. ConflictAlgorithm? conflictAlgorithm
  6. }
  7. );
  1. //插入数据
  2. Future<int>insert(Task task)async{
  3. Database db=await database;
  4. print("insert function called");
  5. print("插入的数据:${task.toJson()}");
  6. /*insert方法会返回最后的行id*/
  7. return await db.insert(_ALLTask, task.toJson());
  8. }
2、Database.rawInsert
 Future<int> rawInsert(String sql, [List<Object?>? arguments]);
  1. //插入数据——法二 rawInsert
  2. Future<int> rawInsert(Task task) async{
  3. Database db=await database;
  4. return await db.rawInsert(
  5. "INSERT INTO $_ALLTask (content, ownType, startDate, endDate, createTime,
  6. completeTime, repeat, isCompleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",[task.content,task.ownType,task.startDate,task.endDate,task.createTime,task.completeTime,task.repeat,task.isCompleted]);
  7. }

删除:

使用 whereArgs 将参数传递给 where 语句。有助于防止 SQL 注入攻击 

 Future<int> delete(String table, {String? where, List<Object?>? whereArgs});
  1. Future delete(Task task)async{
  2. Database db=await database;
  3. print("delete function called!");
  4. await db.delete(_ALLTask,where: "id=?",whereArgs: [task.id]);
  5. }

修改:

1、Database.update
  1. Future<int> update(String table, Map<String, Object?> values,
  2. {String? where,
  3. List<Object?>? whereArgs,
  4. ConflictAlgorithm? conflictAlgorithm});
  1. //修改任务内容(全部数据)
  2. Future update(Task task)async{
  3. Database db=await database;
  4. return db.update(_ALLTask, task.toJson(), where: 'id=?', whereArgs: [task.id]);
  5. }
2、Database.rawUpdate
 Future<int> rawUpdate(String sql, [List<Object?>? arguments]);
  1. Future rawUpdate(Task task,newDate) async{
  2. Database db=await database;
  3. return db.rawUpdate(
  4. '''
  5. UPDATE $_ALLTask
  6. SET createTime=?
  7. WHERE id=?
  8. ''',
  9. [newDate,task.id]
  10. );

 查询:

1、Database.query
  1. Future<List<Map<String, Object?>>> query(String table,
  2. {bool? distinct,
  3. List<String>? columns,
  4. String? where,
  5. List<Object?>? whereArgs,
  6. String? groupBy,
  7. String? having,
  8. String? orderBy,
  9. int? limit,
  10. int? offset});
  1. //查询数据
  2. /*查询到后返回的是一个List<Map<String,Object?>>类型的列表,每一个元素都是Map<String,Object?>
  3. *result就是List<Map<String,Object?>>类型的列表
  4. *result.map((taskMap) => Task.fromJson(taskMap))=======>遍历每一个元素,将每一个元素都执行
  5. *给定的函数,此处是Task.fromJson(taskMap),然后返回一个新的迭代器
  6. *所以这个迭代器里的每一个元素都转换成了Task类型
  7. *.toList();将这个迭代器转换成列表
  8. * 所以最后就返回了一个Task类型的列表 */
  9. Future<List<Task>> query() async{
  10. Database db=await database;
  11. print("query function called!");
  12. var result=await db.query(_ALLTask);
  13. /*此时返回的是一个List<Task>类型*/
  14. return result.map((taskMap) => Task.fromJson(taskMap)).toList();
  15. }
2、Database.rawQuery
  1. Future<List<Map<String, Object?>>> rawQuery(
  2. String sql,
  3. [List<Object?>? arguments]);
  1. Future<List<Task>> rawquery() async{
  2. Database db=await database;
  3. print("query function called!");
  4. var result=await db.rawQuery("SELECT * FROM $_ALLTask ");
  5. /*此时返回的是一个List<Task>类型*/
  6. return result.map((taskMap) => Task.fromJson(taskMap)).toList();
  7. }

查询时排序:

ASC:表示按升序排序。 DESC:表示按降序排序。

var result=await db.query(_ALLTask,orderBy: "datetime(createTime) ASC");
var result=await db.rawQuery("SELECT * FROM $_ALLTask ORDER BY datetime(createTime) DESC");

此时我想按照字段 createTime进行升序、降序。但是字段createTime是String类型。

SQLite中支持多种日期时间格式,但是建议使用ISO 8601格式来存储日期时间值。ISO 8601是一种国际标准,用于表示日期、时间和日期时间值。它的格式如下:

YYYY-MM-DDTHH:MM:SS.SSSZ

其中,YYYY表示年份,MM表示月份,DD表示日期,T表示时间分隔符,HH表示小时,MM表示分钟,SS表示秒,.SSS表示毫秒(可选),Z表示时区偏移量

我的时间格式如下:2023-07-12--11:23:02

  1. String timeStr = "2023-07-12--11:23:02";
  2. //首先使用replaceAll()方法将时间字符串中的--替换为T,以便它符合ISO 8601格式
  3. //使用DateTime.parse()方法将字符串解析为日期时间类型。
  4. DateTime dateTime = DateTime.parse(timeStr.replaceAll("--", "T"));
  5. //使用toIso8601String()方法将日期时间类型格式化为ISO 8601字符串
  6. String iso8601Str = dateTime.toIso8601String();
  7. print("${iso8601Str}");

最后打印出的数据:2023-07-12T15:26:31.000

在现有数据表中插入新列并设置默认值

  1. Future<void> addColumn() async {
  2. Database db = await database;
  3. await db.execute("ALTER $_ALLTask task ADD COLUMN priority INTEGER DEFAULT 0");
  4. }

 默认值此时为0,如果不设置默认值的话,默认值全部为null

在priority默认值全部为null的时候修改为0:

  1. //设置新列的默认值
  2. Future setdefaultValue()async{
  3. Database db = await database;
  4. await db.execute("UPDATE $_ALLTask SET priority = ? WHERE priority IS ?",[0,null]);
  5. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/483226
推荐阅读
相关标签
  

闽ICP备14008679号