Flutter数据库的使用方法
作者:如此风景 发布时间:2022-02-04 04:18:02
说明
Flutter原生是没有支持数据库操作的,它使用SQLlit插件来使应用具有使用数据库的能力。其实就是Flutter通过插件来与原生系统沟通,来进行数据库操作。
平台支持
FLutter的SQLite插件支持IOS,安卓,和MacOS平台
如果要对Linux / Windows / DartVM进行支持请使用sqflite_common_ffi
不支持web平台
数据库操作在安卓或ios的后台执行
使用案例
notepad_sqflite 可以在iOS / Android / Windows / linux / Mac上运行的简单的记事本应用
简单使用
添加依赖
为了使用 SQLite 数据库,首先需要导入 sqflite 和 path 这两个 package
sqflite 提供了丰富的类和方法,以便你能便捷实用 SQLite 数据库。
path 提供了大量方法,以便你能正确的定义数据库在磁盘上的存储位置。
dependencies:
sqflite: ^1.3.0
path:版本号
使用
导入 sqflite.dart
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
打开数据库
SQLite数据库就是文件系统中的文件。如果是相对路径,则该路径是getDatabasesPath()所获得的路径,该路径关联的是Android上的默认数据库目录和iOS上的documents目录。
var db = await openDatabase('my_db.db');
许多时候我们使用数据库时不需要手动关闭它,因为数据库会在程序关闭时被关闭。如果你想自动释放资源,可以使用如下方式:
await db.close();
执行原始的SQL查询
使用getDatabasesPath()获取数据库位置
使用 sqflite package 里的 getDatabasesPath 方法并配合 path package里的 join 方法定义数据库的路径。使用path包中的join方法是确保各个平台路径正确性的最佳实践。
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'demo.db');
打开数据库
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');
});
删
删除表中的一条数据
count = await database
.rawDelete('DELETE FROM Test WHERE name = ?', ['another name']);
改
修改表中的数据
int count = await database.rawUpdate('UPDATE Test SET name = ?, value = ? WHERE name = ?',
['updated name', '9876', 'some name']);
print('updated: $count');
查
查询表中的数据
// Get the records
List<Map> list = await database.rawQuery('SELECT * FROM Test');
List<Map> expectedList = [
{'name': 'updated name', 'id': 1, 'value': 9876, 'num': 456.789},
{'name': 'another name', 'id': 2, 'value': 12345678, 'num': 3.1416}
];
print(list);
print(expectedList);
查询表中存储数据的总条数
count = Sqflite.firstIntValue(await database.rawQuery('SELECT COUNT(*) FROM Test'));
关闭数据库
await database.close();
删除数据库
await deleteDatabase(path);
使用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,以供外部使用
Map<String, Object?> toMap() {
var map = <String, Object?>{
columnTitle: title,
columnDone: done == true ? 1 : 0
};
if (id != null) {
map[columnId] = id;
}
return map;
}
//无参构造
Todo();
//把map类型的数据转换成当前类对象的构造函数。
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(),conflictAlgorithm: ConflictAlgorithm.replace,);
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;
上面查询结果的列表中Map为只读数据,修改此数据会抛出异常
mapRead['my_column'] = 1;
// Crash... `mapRead` is read-only
创建map副本并修改其中的字段
// 根据上面的map创建一个map副本
Map<String, Object?> map = Map<String, Object?>.from(mapRead);
// 在内存中修改此副本中存储的字段值
map['my_column'] = 1;
把查询出来的List< map>类型的数据转换成List< Todo>类型,这样我们就可以痛快的使用啦。
// Convert the List<Map<String, dynamic> into a List<Todo>.
return List.generate(maps.length, (i) {
return Todo(
id: maps[i][columnId],
title: maps[i][columnTitle],
done: maps[i][columnDown],
);
});
批处理
您可以使用批处理来避免dart与原生之间频繁的交互。
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();
获取每个操作的结果是需要成本的(插入的Id以及更新和删除的更改数)。如果您不关心操作的结果则可以执行如下操作关闭结果的响应
await batch.commit(noResult: true);
事务中使用批处理
在事务中进行批处理操作,当事务提交后才会提交批处理。
await database.transaction((txn) async {
var batch = txn.batch();
// ...
// commit but the actual commit will happen when the transaction is committed
// however the data is available in this transaction
await batch.commit();
// ...
});
批处理异常忽略
默认情况下批处理中一旦出现错误就会停止(未执行的语句则不会被执行了),你可以忽略错误,以便后续操作的继续执行。
await batch.commit(continueOnError: 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"
支持的存储类型
由于尚未对值进行有效性检查,因此请避免使用不受支持的类型。参见:
不支持DateTime类型,可将它存储为int或String
不支持bool类型,可存储为int类型 0:false,1:true
SQLite类型 | dart类型 | 值范围 |
---|---|---|
integer | int | 从-2 ^ 63到2 ^ 63-1 |
real | num | |
text | String | |
blob | Uint8List |
参考
sqflile官方地址
flutter对使用SQLite进行数据存储官方介绍文档
来源:https://juejin.cn/post/6959108366811529252


猜你喜欢
- (Memory Leak,内存泄漏)为什么会产生内存泄漏?当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致
- 简单的实现了一个树的结构,很不完善!后续参考一些其他代码的实现。试图实现叶子存在可变的节点,能够用来解析xml文件。叶子的代码:packag
- jQuery的方法连缀使用起来非常方便,可以简化语句,让代码变得清晰简洁。那C#的类方法能不能也实现类似的功能呢?基于这样的疑惑,研究了一下
- 前言表单提交是最常见的数据提交方式,我们经常会填写表单信息,比如用户名,身份证,手机号等等,因此就会产生身份证是否合法,用户名是否为空,虽然
- 本文实例为大家分享了Java实现员工管理系统的具体代码,供大家参考,具体内容如下本系统主要练习到的相关内容: 1、 流程控制语句 2、 类、
- JAVA的 * 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤
- 解决方案1:禁用缓存,前一次使用的方法,在电脑上各浏览器都没问题,但在ipad、安卓手机上仍有问题解决方案2:禁用浏览器后退键 javasc
- 前言本文主要介绍了关于android实现一键锁屏和一键卸载的相关内容,分享出来供大家参考学习,这两个功能也是大家在开发中会遇到的两个需求,下
- 本文实例讲述了C#获取上个月第一天和最后一天日期的方法。分享给大家供大家参考。具体实现代码如下:int year = DateTime.No
- 在 Java 中,null 是一个表示“空值”的特殊值。相信大家都很了解 null 在 Java 中
- 概述递归:指在当前方法内调用自己的这种现象。递归的分类:递归分为两种,直接递归和间接递归。直接递归称为方法自身调用自己。间接递归可以A方法调
- DateTime dt = DateTime.Now;Label1.Text = dt.ToString();//2005-11-5 13:
- Android动画 实现开关按钮动画(属性动画之平移动画),最近做项目,根据项目需求,有一个这样的功能,实现类似开关的动画效果,经过自己琢磨
- 前言本文主要介绍了关于unity代码C#封装为dll的相关内容,分享出来供需要的朋友们学习,下面话不多说了,来一起学习学习吧。方法如下1 V
- 安装方式:使用vs自带的nuget管理工具,搜索AutoMapper ,选择第一个安装到你的项目即可。先说说DTODTO是个什么东东?DTO
- gateway网关与前端请求的跨域问题最近因项目需要,引入了gateway网关。可是发现将前端请求的端口指向网关后,用postman发送请求
- Properties属性文件中的值有等号和换行Spring配置Shiro的过滤器时,有个filterChainDefinitions属性,值
- 窗口之间通信无非有两个方向,主窗口发送数据到副窗口,副窗口发送数据到主窗口。代码在最下面,先看一下解决的思路。首先是简单的主窗口传数据到副窗
- 使用包管理器package com.liunan.myfirstapp.util;import android.content.Contex
- 在JAVA中通过synchronized语句可以实现多线程并发。使用同步代码块,JVM保证同一时间只有一个线程可以拥有某一对象的锁。锁机制实