Flutter数据库管理插件dsqlite的使用
Flutter数据库管理插件dsqlite的使用
简介
dsqlite
包提供了一个 package:database_sql
的实现驱动。sql
包必须与 package:database_sql
包一起使用。
该包提供了 package:database_sql
包的一个完整实现。当在网页浏览器上运行时,它使用由 emscripten
编译自 sqlite3
源代码的 WebAssembly API。所有主要的现代浏览器如 Chrome、Firefox 和 Safari 都支持。
重要事项
网页版在此包中使用的是通过 emscripten
运行输出的 WebAssembly。尽管它可以工作,但性能相比原生的 IndexedDB JavaScript 要慢很多。此外,所有 SQLite 数据都会加载到内存中,导致应用程序消耗大量的内存。另外,emscripten
没有提供官方文档来实现与浏览器本地文件系统 API 结合的文件系统 API,这可能会显著减少虚拟文件系统的内存需求。有关 emscripten
的问题跟踪请求和更好的 API 来实现自定义文件系统 API:
下一步
- 探索可能性通过或不通过 15041 实现 VFS。
- 探索可能性通过模块或虚拟表实现将数据重定向到 IndexedDB。缺点是当使用虚拟表时,不是所有的 SQLite 特性都受支持。
- 将 SQLite 功能移动到 Web Worker 或 Service Worker 而不是主线程。
使用方法
在 pubspec.yaml
中添加 package:database_sql
和 dsqlite
。
dependencies:
database_sql: 0.0.1
dsqlite: 0.0.1
以下是一个完整的示例:
import 'package:database_sql/database_sql.dart' as sqldb;
import 'package:dsqlite/dsqlite.dart' as dsqlite;
void main() async {
// 注册驱动(应该在不同的隔离进程中注册)
sqldb.registerDriver(
'sqlite3',
await dsqlite.Driver.initialize(
// 在 iOS 上无需提供路径。
// 在 Web 上,与 iOS 相同,Web 需要 index.html 来加载 JavaScript 文件和 WebAssembly
path: '/* 路径到动态库 */',
// 连接钩子是可选的,但在需要注册钩子事件或创建自定义函数时非常有用
connectHook: (driver, db, ds) {
/* 你的初始化代码,例如注册函数、钩子事件 */
},
),
);
// 确保即使发生异常,数据库连接也能正确关闭
await sqldb.protect('sqlite3', dsqlite.DataSource('test.db'), block: (db) async {
await db.exec('CREATE TABLE sample(id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT);');
final changes = await db.exec("INSERT INTO sample(text) VALUES('sqlite3-driver')");
// 应该打印 'Last ID: 1, affected: 1'
print('Last ID: ${changes.lastInsertId}, affected: ${changes.rowsAffected}');
// 确保查询结果即使在异常情况下也能正确关闭
await db.protectQuery<sqldb.Row>('SELECT * FROM sample;', block: (rows) async {
while (rows.moveNext()) {
// 应该打印 'ID: 1, text: sqlite3-driver'
print('ID: ${rows.current.intValueBy('id')}, text: ${rows.current.stringValueBy('text')}');
}
});
});
}
编译 WebAssembly
从 sqlite3
源代码使用 emscripten
编译 WebAssembly。在你能够编译 WebAssembly 之前,必须安装并配置好 emscripten
并将其添加到你的 PATH 环境变量中。有关如何安装 emscripten
的详细信息,请参阅 Getting Started。
安装好 emscripten
后,运行以下命令以生成 WebAssembly 文件。默认情况下,它使用包内预定义的构建配置。这些设置会生成多个格式为 ASM 和 WASM 的输出,并且包括对 JavaScript bigint
支持和不支持的版本。
dart run dsqlite -b -d --release 2021:3.36.00 -o /* 存储生成文件的目录 */
如果要禁用多构建或包含自己的优化,则可以提供自定义构建配置。在你的根存储库中创建一个名为 sqlite_webassembly_build.yaml
的配置文件。
# 这是在网页浏览器中运行时 dsqlite 使用的最小运行时导出 API
# 如果你的应用不使用 UTF-16,则可以省略 `UTF16ToString`、`stringToUTF16` 和 `lengthBytesUTF16` API。同样的二进制文件或 Blob 可以省略 `writeArrayToMemory`。
# 如果你的应用不需要 sqlite3 API,如创建函数或回调,则可以省略 `addFunction` 和 `removeFunction`。这样可以将生成的 JavaScript 大小稍微减小一些。
runtime:
- 'cwrap'
- 'setValue'
- 'getValue'
- 'UTF8ToString'
- 'stringToUTF8'
- 'UTF16ToString'
- 'stringToUTF16'
- 'writeArrayToMemory'
- 'lengthBytesUTF8'
- 'lengthBytesUTF16'
- 'addFunction'
- 'removeFunction'
emitBitInt: true # true 用于生成同时支持和不支持 bigint 的版本
# 如果未提供,将使用此包内的配置文件中的 cflag。
# cflag:
# - '-O2'
# - '-DSQLITE_THREADSAFE=0' # JavaScript 是单线程的
# 如果未提供,将使用此包内的配置文件中的 emflag。
# emflag:
# 如果未提供,将使用此包内的配置文件中的 release。
# 这是 emcc 用于发布构建的标志
# release:
# 如果未提供,将使用此包内的配置文件中的 debug。
# 这是 emcc 用于调试构建的标志
# debug:
# 提供 wasm 键以指示生成的输出为 WebAssembly wasm 格式
# 必须提供 emcc 标志
# wasm:
# - '-s ALLOW_MEMORY_GROWTH=1'
# 提供 asm 键以指示生成的输出为 asm.js 格式
# 必须提供 emcc 标志
# asm:
# - '-s ALLOW_MEMORY_GROWTH=1'
测试
在运行测试之前,需要下载 SQLite 源代码并进行本地构建,因此你必须安装 emscripten
和必要的工具来从源代码构建 SQLite。
测试代码期望动态库的位置为:
# Windows 平台
sqlite/latest/sqlite3.dll
# Linux,此库由 sqlite3 源代码 Makefile 自动生成。
sqlite/latest/.libs/libsqlite3.so
# macOS,此库由 sqlite3 源代码 Makefile 自动生成。
sqlite/latest/.libs/libsqlite3.dylib
对于 Windows 用户,需要手动构建 SQLite3 库 sqlite3.dll
。请遵循 sqlite 官方文档的描述 这里。
对于其他平台的用户,使用 make 构建和运行测试。Windows 用户可以使用 powershell
或其他命令行应用程序来运行 makefile。
make requisition
# 测试浏览器(仅调试 WebAssembly)和 VM
make dsqlite_test_local
# 全面测试浏览器(调试和发布)以及 VM
make dsqlite_test
更多关于Flutter数据库管理插件dsqlite的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter数据库管理插件dsqlite的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中,如果你需要使用SQLite数据库进行本地数据存储,可以使用 sqflite
插件。sqflite
是一个流行的Flutter插件,它提供了对SQLite数据库的访问和管理功能。以下是使用 sqflite
插件的基本步骤:
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 sqflite
和 path
依赖:
dependencies:
flutter:
sdk: flutter
sqflite: ^2.0.0+4
path: ^1.8.0
然后运行 flutter pub get
来安装依赖。
2. 创建数据库
接下来,你可以创建一个数据库并定义表结构。以下是一个简单的例子:
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
static Database? _database;
factory DatabaseHelper() {
return _instance;
}
DatabaseHelper._internal();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
String path = join(await getDatabasesPath(), 'my_database.db');
return await openDatabase(
path,
version: 1,
onCreate: _onCreate,
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE my_table(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
);
}
}
3. 插入数据
你可以使用 insert
方法向数据库中插入数据:
Future<void> insertData(String name, int age) async {
final db = await DatabaseHelper().database;
await db.insert(
'my_table',
{'name': name, 'age': age},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
4. 查询数据
你可以使用 query
方法从数据库中查询数据:
Future<List<Map<String, dynamic>>> getData() async {
final db = await DatabaseHelper().database;
return await db.query('my_table');
}
5. 更新数据
你可以使用 update
方法更新数据库中的数据:
Future<void> updateData(int id, String name, int age) async {
final db = await DatabaseHelper().database;
await db.update(
'my_table',
{'name': name, 'age': age},
where: 'id = ?',
whereArgs: [id],
);
}
6. 删除数据
你可以使用 delete
方法删除数据库中的数据:
Future<void> deleteData(int id) async {
final db = await DatabaseHelper().database;
await db.delete(
'my_table',
where: 'id = ?',
whereArgs: [id],
);
}
7. 关闭数据库
在应用程序退出时,你可以关闭数据库以释放资源:
Future<void> closeDatabase() async {
final db = await DatabaseHelper().database;
await db.close();
}
示例代码
以下是一个完整的示例代码,展示了如何使用 sqflite
插件进行数据库操作:
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('SQLite Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
await DatabaseHelper().insertData('Alice', 30);
print('Data inserted');
},
child: Text('Insert Data'),
),
ElevatedButton(
onPressed: () async {
List<Map<String, dynamic>> data = await DatabaseHelper().getData();
print('Data: $data');
},
child: Text('Query Data'),
),
ElevatedButton(
onPressed: () async {
await DatabaseHelper().updateData(1, 'Alice Smith', 31);
print('Data updated');
},
child: Text('Update Data'),
),
ElevatedButton(
onPressed: () async {
await DatabaseHelper().deleteData(1);
print('Data deleted');
},
child: Text('Delete Data'),
),
],
),
),
),
);
}
}
class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
static Database? _database;
factory DatabaseHelper() {
return _instance;
}
DatabaseHelper._internal();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
String path = join(await getDatabasesPath(), 'my_database.db');
return await openDatabase(
path,
version: 1,
onCreate: _onCreate,
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE my_table(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
);
}
Future<void> insertData(String name, int age) async {
final db = await database;
await db.insert(
'my_table',
{'name': name, 'age': age},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<Map<String, dynamic>>> getData() async {
final db = await database;
return await db.query('my_table');
}
Future<void> updateData(int id, String name, int age) async {
final db = await database;
await db.update(
'my_table',
{'name': name, 'age': age},
where: 'id = ?',
whereArgs: [id],
);
}
Future<void> deleteData(int id) async {
final db = await database;
await db.delete(
'my_table',
where: 'id = ?',
whereArgs: [id],
);
}
}