Flutter之数据库的使用(sqflite_common_ffi)

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()

class DBHelper {


  //定义了一个静态变量---_dbHelper,保存DBHelper类的单例实例
  static DBHelper? _dbHelper;

  //定义了一个静态方法---getInstance()获取DBHelper的单例实例
  //如果_dbHelper为空,就创建一个新的DBHelper实例
  static DBHelper getInstance() {
    if (_dbHelper == null) {
      _dbHelper = DBHelper();
    }
    return _dbHelper!;
  }

  //_db是一个Database类型的成员,用于存储数据库实例
  Database? _db;

  //数据库中的表
  static final String _ALLTask = "_ALLTask"; //所有任务

  //database是一个异步getter,它返回数据库实例。如果_db为空,就调用initDB方法初始化数据库。
  Future<Database> get database async {
    if (_db != null) {
      return _db!;
    }
    _db = await initDB();
    return _db!;
  }

}

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(打开数据库操作)的某些属性:

  /// Specify the expected version.
  int? version;

  /// called right after opening the database.(打开数据库后立即调用)
  OnDatabaseConfigureFn? onConfigure;

  /// Called when the database is created.(数据库创建时的回调函数)
  OnDatabaseCreateFn? onCreate;

  /// Called when the database is upgraded.(数据库升级时调用)
  OnDatabaseVersionChangeFn? onUpgrade;

  /// Called when the database is downgraded.(数据库降级时调用)
  ///
  /// Use [onDatabaseDowngradeDelete] for re-creating the database
(使用 [onDatabaseDowngradeDelete] 重新创建数据库)
  OnDatabaseVersionChangeFn? onDowngrade;

  /// Called after all other callbacks have been called.(在调用所有其他回调后调用)
  OnDatabaseOpenFn? onOpen;

  /// Open the database in read-only mode (no callback called).(以只读模式打开数据库(不调用回调)。)
  late bool readOnly;

  /// The existing single-instance (hot-restart)(现有单实例(热重启))
  late bool singleInstance;

 //初始化数据库
  initDB()async{
    //1、初始化数据库
    sqfliteFfiInit();
   
    //2、获取databaseFactoryFfi对象
    var databaseFactory = databaseFactoryFfi;
    
    //3、创建数据库
    return await databaseFactory.openDatabase(

        //数据库路径
        path.join(await databaseFactory.getDatabasesPath(), "TO-DO.db"),

        //打开数据库操作
        options: OpenDatabaseOptions(
          //版本
          version: 5,
          //创建时操作
          onCreate: (db,version)async{
            print("创建数据库");
            return await db.execute(
              "CREATE TABLE $_ALLTask ("
                  "id INTEGER PRIMARY KEY AUTOINCREMENT,"
                  "content TEXT,"
                  "ownType STRING,"
                  "startDate STRING,"
                  "endDate STRING,"
                  "createTime STRING,"
                  "completeTime STRING,"
                  "repeat STRING,"
                  "isCompleted INTEGER"
                  ")"
            );
          }
        )
      );
  }

至此,数据库创建完成。

数据库操作

增加:

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

删除:

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

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

修改:

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

    Database db=await database;
    return db.rawUpdate(
      '''
      UPDATE $_ALLTask
      SET createTime=?
      WHERE id=?
      ''',
      [newDate,task.id]
    );

 查询:

1、Database.query
Future<List<Map<String, Object?>>> query(String table,
      {bool? distinct,
      List<String>? columns,
      String? where,
      List<Object?>? whereArgs,
      String? groupBy,
      String? having,
      String? orderBy,
      int? limit,
      int? offset});
  //查询数据
  /*查询到后返回的是一个List<Map<String,Object?>>类型的列表,每一个元素都是Map<String,Object?>
   *result就是List<Map<String,Object?>>类型的列表
   *result.map((taskMap) => Task.fromJson(taskMap))=======>遍历每一个元素,将每一个元素都执行 
   *给定的函数,此处是Task.fromJson(taskMap),然后返回一个新的迭代器
   *所以这个迭代器里的每一个元素都转换成了Task类型
   *.toList();将这个迭代器转换成列表
   * 所以最后就返回了一个Task类型的列表  */

  Future<List<Task>> query() async{
    Database db=await database;
    print("query function called!");
    var result=await db.query(_ALLTask);
   /*此时返回的是一个List<Task>类型*/
    return result.map((taskMap) => Task.fromJson(taskMap)).toList();
    
  }
2、Database.rawQuery
Future<List<Map<String, Object?>>> rawQuery(
      String sql,
      [List<Object?>? arguments]);
Future<List<Task>> rawquery() async{
    Database db=await database;
    print("query function called!");
    var result=await db.rawQuery("SELECT * FROM $_ALLTask ");
   /*此时返回的是一个List<Task>类型*/
    return result.map((taskMap) => Task.fromJson(taskMap)).toList();
    
  }

查询时排序:

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

String timeStr = "2023-07-12--11:23:02";

//首先使用replaceAll()方法将时间字符串中的--替换为T,以便它符合ISO 8601格式
//使用DateTime.parse()方法将字符串解析为日期时间类型。
DateTime dateTime = DateTime.parse(timeStr.replaceAll("--", "T"));

//使用toIso8601String()方法将日期时间类型格式化为ISO 8601字符串
String iso8601Str = dateTime.toIso8601String();

print("${iso8601Str}");

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

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

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

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

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

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值