Flutter 持久化存储之数据库存储进阶

前言

在移动端,持久化存储需求越来越普遍,无论是单机模式还是配置数据,都离不开存储,SharedPreferences可以解决大部分的问题,但是比较复杂的数据就很难处理了,这里使用关系型数据库SQLite,Flutter中也有对SQLite数据库的插件sqflite,经过长期测试,其实Flutter中使用SQLite还是有很多问题,这里整理一下使用方法,尽量规避这些问题。

程序架构图

使用方法

SQLite官方网址 SQLite Home Page,语法和数据类型可以在官方搜索,都有使用示例。

1. 首先,在pubspec.yaml中引用插件,可以先使用sqflite: any,下载后在pubspec.lock找到对应的版本号,再修改为sqflite: ^xxxxx,为了稳定性,版本号一般要提前定好,避免因插件版本更新导致程序出现问题。

  #数据库
  sqflite: ^1.3.2+4

2. 定义操作帮助类SqlProvider,这里只有openDatabase,没有close,因为插件并不能很好的控制数据库的开关,经常出现未关闭的情况导致程序错误,综合考虑,一直开启。直接注册到全局变量Global类,方便业务层调用。

// ignore_for_file: file_names, import_of_legacy_library_into_null_safe, depend_on_referenced_packages, slash_for_doc_comments, unnecessary_null_comparison
import 'package:hsware_admin_app1/models/test_table1_model.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

class SqlProvider {
  Database? database;
  static const dbName = 'myDb.db';

  //初始化
  open() async {
    var databasePath = await getDatabasesPath();
    String path = join(databasePath, dbName);
    database = await openDatabase(path, version: 1,
        onCreate: (Database db, int version) async {
        // When creating the db, create the table
        await db.execute(TestTable1Model.createTableSql);
    });
  }

  //判断表是否存在
  isTableExits(String tableName) async {
    //内建表sqlite_master
    var sql =
        "SELECT * FROM sqlite_master WHERE TYPE = 'table' AND NAME = '$tableName'";
    List<Map<String, dynamic>>? res = await database?.rawQuery(sql);
    var returnRes = res != null && res.isNotEmpty;
    return returnRes;
  }

  /** 
   * * description: query查询所有
   * * param1 tableName:表名
   * * param2 listColumns:列集合
   * * return {List<Map<String, dynamic>>}
   */
  query(String tableName, List<String> listColumns) async {
    //var myColumns = ['name', 'type'];
    var result = await database?.query(tableName, columns: listColumns);
    return result;
  }

  /** 
   * * description: query根据条件查询
   * * param1 tableName:表名
   * * param2 listColumns:列集合
   * * param3 where:条件,'Id = ?'
   * * param4 whereArgs:条件值,var myColumns = ['name', 'type'];
   * * return {List<Map<String, dynamic>>}
   */
  queryByWhere(String tableName, List<String> listColumns, String where,
      List<dynamic> whereArgs) async {
    //var myColumns = ['name', 'type'];
    var result = await database?.query(tableName,
        columns: listColumns, where: where, whereArgs: whereArgs);
    return result;
  }

  /** 
   * * description: 根据sql语句查询数据
   * * return {sql语句}
   */
  queryBysql(String sql) async {
    return await database?.rawQuery(sql);
  }

  /** 
   * * description: insert
   * * param1 tableName:表名
   * * param2 values:列集合
   * * return {dynamic }
   */
  insert(String tableName, Map<String, dynamic> values) async {
    //var values = {'name':'my_name','type':'my_type'};
    await database?.insert(tableName, values);
  }

  /** 
   * * description: 更新
   * * param1 sql:语句,String update_sql = "UPDATE user SET username = ? WHERE id = ?";
   * * param2 arguments:列值,['paul','1']
   * * return {dynamic}
   */
  rawUpdate(String sql, List<dynamic> arguments) async {
    await database?.rawUpdate(sql, arguments);
  }

  /** 
   * * description: delete
   * * param1 tableName:表名
   * * param2 where:条件,'Id = ?'
   * * param3 whereArgs:条件值,var myColumns = ['name', 'type'];
   * * return {dynamic}
   */
  delete(String tableName, String where, List<dynamic> whereArgs) async {
    //var myWhere = 'name = ?';
    //var myArgs = ['cat'];
    await database?.delete(tableName, where: where, whereArgs: whereArgs);
  }

  /** 
   * * description: deleteAll
   * * param1 tableName:表名
   * * return {dynamic}
   */
  deleteAll(String tableName) async {
    await database?.delete(tableName);
  }

  ///关闭
  // close() async {
  //   database?.close();
  //   database = null;
  // }
}

3. 实体类TestTable1Model

注意点:

3.1 如果使用json转dart工具,将fromJson(dynamic json) 修改为fromJson(Map<String, dynamic> json),只有使用生成dart工具才会注意这个问题。

3.2 todo 修改实体,这部分的内容只有该实体要持久化存储时才会配置,分别是表名、列名、查询列名集合、创建表语句。

// ignore_for_file: file_names
// ignore: slash_for_doc_comments
/**
 * * Author: Hou
 * * Date: 2022-08-25 14:29:02
 * * LastEditors: Hou
 * * LastEditTime: 2022-09-29 18:08:00
 * * Description: Table1测试实体
 * * [属性]: 
 * * 
 * ```dart
 * ///pkid
 * num? _id;
 * ///列1
 * String? _column1;
 * ///列2
 * String? _column2;
 * ///列3
 * String? _column3;
 * ///列4
 * String? _column4;
 * ///列5
 * String? _column5;
 * ///列6
 * String? _column6;
 * ///列7
 * String? _column7;
 * ///列8
 * String? _column8;
 * ///列9
 * String? _column9;
 * ///列10
 * String? _column10;
 * ```
 * */
class TestTable1Model {
  TestTable1Model(
      {num? id,
      String? column1,
      String? column2,
      String? column3,
      String? column4,
      String? column5,
      String? column6,
      String? column7,
      String? column8,
      String? column9,
      String? column10}) {
    _id = id;
    _column1 = column1;
    _column2 = column2;
    _column3 = column3;
    _column4 = column4;
    _column5 = column5;
    _column6 = column6;
    _column7 = column7;
    _column8 = column8;
    _column9 = column9;
    _column10 = column10;
  }

  TestTable1Model.fromJson(Map<String, dynamic> json) {
    _id = json['Id'];
    _column1 = json['Column1'];
    _column2 = json['Column2'];
    _column3 = json['Column3'];
    _column4 = json['Column4'];
    _column5 = json['Column5'];
    _column6 = json['Column6'];
    _column7 = json['Column7'];
    _column8 = json['Column8'];
    _column9 = json['Column9'];
    _column10 = json['Column10'];
  }

  ///pkid
  num? _id;

  ///列1
  String? _column1;

  ///列2
  String? _column2;

  ///列3
  String? _column3;

  ///列4
  String? _column4;

  ///列5
  String? _column5;

  ///列6
  String? _column6;

  ///列7
  String? _column7;

  ///列8
  String? _column8;

  ///列9
  String? _column9;

  ///列10
  String? _column10;

  //todo 修改实体  start

  // 定义数据库Mock模型
  static String tableName = 'TestTable1';

  //字段
  static String columnId = 'Id';
  static String columnColumn1 = 'Column1';
  static String columnColumn2 = 'Column2';
  static String columnColumn3 = 'Column3';
  static String columnColumn4 = 'Column4';
  static String columnColumn5 = 'Column5';
  static String columnColumn6 = 'Column6';
  static String columnColumn7 = 'Column7';
  static String columnColumn8 = 'Column8';
  static String columnColumn9 = 'Column9';
  static String columnColumn10 = 'Column10';

  //查询时的列
  static List<String> listColumns = [
    columnId,
    columnColumn1,
    columnColumn2,
    columnColumn3,
    columnColumn4,
    columnColumn5,
    columnColumn6,
    columnColumn7,
    columnColumn8,
    columnColumn9,
    columnColumn10
  ];

  //建表语句
  static String createTableSql = '''
      CREATE TABLE If NOT EXISTS $tableName (
        $columnId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 
        $columnColumn1    TEXT ,
        $columnColumn2  TEXT,
        $columnColumn3   TEXT,
        $columnColumn4   TEXT,
        $columnColumn5   TEXT,
        $columnColumn6   TEXT,
        $columnColumn7   TEXT,
        $columnColumn8   TEXT,
        $columnColumn9   TEXT,
        $columnColumn10   TEXT
      );
      ''';

  //todo 修改实体  end

  TestTable1Model copyWith(
          {num? id,
          String? column1,
          String? column2,
          String? column3,
          String? column4,
          String? column5,
          String? column6,
          String? column7,
          String? column8,
          String? column9,
          String? column10}) =>
      TestTable1Model(
        id: id ?? _id,
        column1: column1 ?? _column1,
        column2: column2 ?? _column2,
        column3: column3 ?? _column3,
        column4: column4 ?? _column4,
        column5: column5 ?? _column5,
        column6: column6 ?? _column6,
        column7: column7 ?? _column7,
        column8: column8 ?? _column8,
        column9: column9 ?? _column9,
        column10: column10 ?? _column10,
      );

  num? get id => _id;

  String? get column1 => _column1;

  String? get column2 => _column2;

  String? get column3 => _column3;

  String? get column4 => _column4;

  String? get column5 => _column5;

  String? get column6 => _column6;
  set setColumn6(String? value) {
    _column6 = value;
  }

  String? get column7 => _column7;

  String? get column8 => _column8;

  String? get column9 => _column9;

  String? get column10 => _column10;

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    //自动标识列
    //map['Id'] = _id;
    map['Column1'] = _column1;
    map['Column2'] = _column2;
    map['Column3'] = _column3;
    map['Column4'] = _column4;
    map['Column5'] = _column5;
    map['Column6'] = _column6;
    map['Column7'] = _column7;
    map['Column8'] = _column8;
    map['Column9'] = _column9;
    map['Column10'] = _column10;
    return map;
  }
}

4. 业务层使用示例

//查询所有数据
List<Map<String, dynamic>> listDb = await Global.sqlProvider
          .query(TestTable1Model.tableName, TestTable1Model.listColumns);
//根据id查询实体,例如id为1001
 List<Map<String, dynamic>> list = await Global.sqlProvider.queryByWhere(
        TestTable1Model.tableName,
        TestTable1Model.listColumns,
        'Id = ?',
        [1001]);
//删除数据,例如id为1001
await Global.sqlProvider.delete(TestTable1Model.tableName,'Id = ?', [1001]);
//新建
TestTable1Model model =TestTable1Model(
    column1:"c1",
    column2:"c2",
    column3:"c3",
    column4:"c4",
    column5:"c5");
await Global.sqlProvider.insert(
    TestTable1Model.tableName, model.toJson());
//修改
String updateSql =
    "UPDATE ${TestTable1Model.tableName} "
    "SET "
    "Column1 = ?,"
    "Column2 = ?,"
    "Column3 = ?,"
    "Column4 = ?,"
    "Column5 = ?,"
    "Column6 = ?"
    " WHERE Id = ?";
await Global.sqlProvider.rawUpdate(updateSql, [
    model.column1,
    model.column2,
    model.column3,
    model.column4,
    model.column5,
    model.column6,
    model.id
        ]);

 后续待优化

posted @ 2022-12-23 11:22  0Behavior  阅读(432)  评论(0编辑  收藏  举报