Flutter本地数据库管理插件sqflite的使用

发布于 1周前 作者 nodeper 来自 Flutter

Flutter本地数据库管理插件sqflite的使用

sqflite简介

pub package

sqflite 是一个用于Flutter的SQLite插件,支持iOS、Android和MacOS。它具有以下特点:

  • 支持事务和批量操作
  • 开启时自动进行版本管理
  • 提供插入/查询/更新/删除等操作的帮助函数
  • 在iOS和Android上,数据库操作在后台线程中执行

其他平台支持:

使用示例:

快速开始

要开始使用sqflite,您需要将其添加到项目中。按照以下步骤操作:

  1. 打开终端进入项目根目录。
  2. 运行以下命令:
    flutter pub add sqflite
    
    此命令将在pubspec.yaml文件中添加一行,并隐式运行flutter pub get。添加的行如下所示:
    dependencies:
      sqflite: 
    

使用示例

导入sqflite.dart

import 'package:sqflite/sqflite.dart';

打开数据库

SQLite数据库是文件系统中的一个文件,由路径标识。如果路径相对,则该路径相对于getDatabasesPath()返回的默认数据库目录(Android)或文档目录(iOS/MacOS)。

var db = await openDatabase('my_db.db');

可以使用基本迁移机制来处理打开期间的模式更改。通常应用程序只需要一个数据库,并且无需关闭它(当应用程序终止时会自动关闭)。如果您想释放资源,可以关闭数据库。

await db.close();

原始SQL查询

以下是执行原始SQL查询的演示代码:

// 获取数据库路径
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'demo.db');

// 删除数据库(可选)
await deleteDatabase(path);

// 打开数据库
Database database = await openDatabase(path, version: 1,
    onCreate: (Database db, int version) async {
  // 创建表
  await db.execute(
      'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)');
});

// 插入记录
await database.transaction((txn) async {
  int id1 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');
  print('inserted1: $id1');
  int id2 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES(?, ?, ?)',
      ['another name', 12345678, 3.1416]);
  print('inserted2: $id2');
});

// 更新记录
int count = await database.rawUpdate(
    'UPDATE Test SET name = ?, value = ? WHERE name = ?',
    ['updated name', '9876', 'some name']);
print('updated: $count');

// 查询所有记录
List<Map> list = await database.rawQuery('SELECT * FROM Test');
print(list);

// 计算记录数量
count = Sqflite.firstIntValue(await database.rawQuery('SELECT COUNT(*) FROM Test'));
assert(count == 2);

// 删除记录
count = await database.rawDelete('DELETE FROM Test WHERE name = ?', ['another name']);
assert(count == 1);

// 关闭数据库
await database.close();

SQL帮助函数

下面是一个使用帮助函数的例子:

final String tableTodo = 'todo';
final String columnId = '_id';
final String columnTitle = 'title';
final String columnDone = 'done';

class Todo {
  int id;
  String title;
  bool done;

  Map<String, Object?> toMap() {
    var map = <String, Object?>{
      columnTitle: title,
      columnDone: done == true ? 1 : 0
    };
    if (id != null) {
      map[columnId] = id;
    }
    return map;
  }

  Todo();

  Todo.fromMap(Map<String, Object?> map) {
    id = map[columnId];
    title = map[columnTitle];
    done = map[columnDone] == 1;
  }
}

class TodoProvider {
  Database db;

  Future open(String path) async {
    db = await openDatabase(path, version: 1,
        onCreate: (Database db, int version) async {
      await db.execute('''
create table $tableTodo ( 
  $columnId integer primary key autoincrement, 
  $columnTitle text not null,
  $columnDone integer not null)
''');
    });
  }

  Future<Todo> insert(Todo todo) async {
    todo.id = await db.insert(tableTodo, todo.toMap());
    return todo;
  }

  Future<Todo> getTodo(int id) async {
    List<Map> maps = await db.query(tableTodo,
        columns: [columnId, columnDone, columnTitle],
        where: '$columnId = ?',
        whereArgs: [id]);
    if (maps.length > 0) {
      return Todo.fromMap(maps.first);
    }
    return null;
  }

  Future<int> delete(int id) async {
    return await db.delete(tableTodo, where: '$columnId = ?', whereArgs: [id]);
  }

  Future<int> update(Todo todo) async {
    return await db.update(tableTodo, todo.toMap(),
        where: '$columnId = ?', whereArgs: [todo.id]);
  }

  Future close() async => db.close();
}

读取结果

假设从数据库中读取的结果如下:

List<Map<String, Object?>> records = await db.query('my_table');

读取的结果是只读的,不能直接修改:

// 获取第一条记录
Map<String, Object?> mapRead = records.first;
// 尝试修改将抛出异常
mapRead['my_column'] = 1;
// 抛出异常:`mapRead` 是只读的

如果要在内存中修改,请创建新的映射:

// 获取第一条记录并创建新映射
Map<String, Object?> map = Map<String, Object?>.from(mapRead);
// 现在可以修改了
map['my_column'] = 1;

事务

不要在事务中使用数据库对象,而应仅使用事务对象访问数据库。注意,回调onCreate onUpgrade onDowngrade 已经被内部包装在一个事务中,因此不需要在此回调中再包装事务。

await database.transaction((txn) async {
  // 正确用法
  await txn.execute('CREATE TABLE Test1 (id INTEGER PRIMARY KEY)');
  
  // 错误用法,这将导致死锁!
  await database.execute('CREATE TABLE Test2 (id INTEGER PRIMARY KEY)');
});

事务提交的前提是回调不抛出错误。如果抛出错误,则事务将被取消。因此可以通过抛出异常来回滚事务。

批量支持

为了避免Dart与原生代码之间的频繁交互,您可以使用Batch

batch = db.batch();
batch.insert('Test', {'name': 'item'});
batch.update('Test', {'name': 'new_item'}, where: 'name = ?', whereArgs: ['item']);
batch.delete('Test', where: 'name = ?', whereArgs: ['item']);
results = await batch.commit();

如果您不关心结果并且担心性能问题,可以使用:

await batch.commit(noResult: true);

警告:在事务期间,批处理不会提交,直到事务提交为止。

表和列名

避免使用SQLite关键字作为实体名称。如果使用了以下名称之一:

"add","all","alter","and","as","autoincrement","between","case","check","collate","commit","constraint","create","default","deferrable","delete","distinct","drop","else","escape","except","exists","foreign","from","group","having","if","in","index","insert","intersect","into","is","isnull","join","limit","not","notnull","null","on","or","order","primary","references","select","set","table","then","to","transaction","union","unique","update","using","values","when","where"

助手将转义这些名称。例如:

db.query('table')

等同于手动添加双引号:

db.rawQuery('SELECT * FROM "table"');

在任何其他原始语句中(包括orderBywheregroupBy),请确保正确地使用双引号转义名称。

支持的SQLite类型

请参考官方文档了解支持的类型。特别注意:

  • DateTime不是SQLite支持的类型,建议存储为整数(时间戳)或字符串(ISO8601格式)
  • bool不是SQLite支持的类型,使用INTEGER类型并以0和1表示

更多信息请参阅支持的类型文档

INTEGER

  • Dart类型:int
  • 支持值:从-2^63到2^63 - 1

REAL

  • Dart类型:num

TEXT

  • Dart类型:String

BLOB

  • Dart类型:Uint8List

当前问题

由于SQLite事务的工作方式(线程),并发读写事务不受支持。所有调用目前都是同步的,并且事务块是排他的。尝试通过多次打开数据库来支持并发访问只在iOS上有效,因为Android重用了相同的数据库对象。另一个潜在的解决方案是在原生线程中访问数据库,但在事务期间这是不允许的。

更多信息

希望这些信息能帮助您更好地理解和使用sqflite插件。如果您有任何问题或需要进一步的帮助,请随时提问!


更多关于Flutter本地数据库管理插件sqflite的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter本地数据库管理插件sqflite的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用sqflite插件进行本地数据库管理的代码示例。sqflite是一个流行的Flutter插件,用于在Android和iOS平台上进行SQLite数据库操作。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加sqflite依赖:

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0+4  # 请检查最新版本号

然后运行flutter pub get来安装依赖。

2. 初始化数据库

在你的Flutter应用中,你需要初始化数据库并打开一个数据库连接。这通常在应用的启动过程中完成。

import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SQFlite Demo'),
        ),
        body: DatabaseDemo(),
      ),
    );
  }
}

class DatabaseDemo extends StatefulWidget {
  @override
  _DatabaseDemoState createState() => _DatabaseDemoState();
}

class _DatabaseDemoState extends State<DatabaseDemo> {
  Database? _db;

  @override
  void initState() {
    super.initState();
    _initializeDatabase().then((_) {
      setState(() {});
    });
  }

  Future<void> _initializeDatabase() async {
    // 打开(或创建)数据库
    _db = await openDatabase(
      // 数据库文件路径
      join(await getDatabasesPath(), 'demo.db'),
      // 当数据库版本改变时调用的回调函数
      version: 1,
      onCreate: (Database db, int version) async {
        // 创建表
        await db.execute(
          'CREATE TABLE User('
          'id INTEGER PRIMARY KEY,'
          'name TEXT,'
          'age INTEGER)',
        );
      },
      // 当数据库打开时调用的回调函数(可选)
      onOpen: (db) {},
    );
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () {
          // 示例:插入数据
          _insertData();
        },
        child: Text('Insert Data'),
      ),
    );
  }

  Future<void> _insertData() async {
    if (_db != null) {
      await _db!.insert(
        'User',
        {'name': 'Alice', 'age': 30},
        conflictAlgorithm: ConflictAlgorithm.replace,
      );
      print('Data inserted');
    }
  }
}

3. 插入、查询、更新和删除数据

以下是如何在Flutter中使用sqflite进行基本的数据库操作:

Future<void> _insertData() async {
  if (_db != null) {
    await _db!.insert(
      'User',
      {'name': 'Bob', 'age': 25},
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
    print('Data inserted');
  }
}

Future<List<Map<String, dynamic>>> _queryData() async {
  List<Map<String, dynamic>> result = [];
  if (_db != null) {
    result = await _db!.query('User');
  }
  return result;
}

Future<void> _updateData() async {
  if (_db != null) {
    await _db!.update(
      'User',
      {'age': 26},
      where: 'name = ?',
      whereArgs: ['Bob'],
    );
    print('Data updated');
  }
}

Future<void> _deleteData() async {
  if (_db != null) {
    await _db!.delete(
      'User',
      where: 'name = ?',
      whereArgs: ['Bob'],
    );
    print('Data deleted');
  }
}

4. 调用数据库操作

你可以将这些数据库操作绑定到UI按钮上,以便在应用中测试它们:

@override
Widget build(BuildContext context) {
  return Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: () {
            _insertData();
          },
          child: Text('Insert Data'),
        ),
        ElevatedButton(
          onPressed: async () {
            List<Map<String, dynamic>> users = await _queryData();
            print(users);
          },
          child: Text('Query Data'),
        ),
        ElevatedButton(
          onPressed: () {
            _updateData();
          },
          child: Text('Update Data'),
        ),
        ElevatedButton(
          onPressed: () {
            _deleteData();
          },
          child: Text('Delete Data'),
        ),
      ],
    ),
  );
}

这个示例展示了如何在Flutter中使用sqflite插件进行基本的数据库操作。你可以根据需要扩展这些功能,例如添加更多的表和字段,处理更复杂的查询等。

回到顶部