flutter 持久化存储-----数据库sqflite
Flutter中持久化存储数据有多种方案, 一般常用的有 shared_preferences 和 sqfite
-
shared_preferences: 包含NSUserDefaults(在iOS上)和SharedPreferences(在Android上),为简单数据提供持久存储。数据以异步方式持久保存到磁盘。
-
sqflite: 是一款轻量级的关系型数据库,类似SQLite. 支持iOS和Android。适用于存储数据库 , 表类型的数据.
sqflite的使用
添加依赖:
作者所用版本为1.1.3
dependencies: ... sqflite: ^1.1.3
为了方便,外面创建一个DatabaseHelper来封装一些数据库的相关操作:
先贴一下代码 ,然后我们逐行分析:
/* * author: Created by 李卓原 on 2019/3/12. * email: zhuoyuan93@gmail.com * */import 'dart:async';import 'package:path/path.dart';import 'package:sale_aggregator_app/models/video.dart';import 'package:sqflite/sqflite.dart';class DatabaseHelper { static final DatabaseHelper _instance = new DatabaseHelper.internal(); factory DatabaseHelper() => _instance; final String tableVideo = 'VideoTable'; final String columnId = 'id'; final String image = 'image'; final String url = 'url'; final String duration = 'duration'; final String title = 'title'; final String favoriteStatus = 'favorite_status'; static Database _db; DatabaseHelper.internal(); Future<Database> get db async { if (_db != null) { return _db; } _db = await initDb(); return _db; } initDb() async { String databasesPath = await getDatabasesPath(); String path = join(databasesPath, 'flashgo.db'); var db = await openDatabase(path, version: 1, onCreate: _onCreate); return db; } void _onCreate(Database db, int newVersion) async { await db.execute( 'CREATE TABLE $tableVideo($columnId INTEGER PRIMARY KEY, $image TEXT, $url TEXT, $duration INTEGER, $title TEXT, $favoriteStatus TEXT)'); } Future<int> insertVideo(Video video) async { var dbClient = await db; var result = await dbClient.insert(tableVideo, video.toJson()); return result; } Future<List> selectVideos({int limit, int offset}) async { var dbClient = await db; var result = await dbClient.query( tableVideo, columns: [columnId, image, url, duration, title, favoriteStatus], limit: limit, offset: offset, ); List<Video> videos = []; result.forEach((item) => videos.add(Video.fromSql(item))); return videos; } Future<int> getCount() async { var dbClient = await db; return Sqflite.firstIntValue( await dbClient.rawQuery('SELECT COUNT(*) FROM $tableVideo')); } Future<Video> getVideo(int id) async { var dbClient = await db; List<Map> result = await dbClient.query(tableVideo, columns: [columnId, image, url, duration, title, favoriteStatus], where: '$id = ?', whereArgs: [id]); if (result.length > 0) { return Video.fromSql(result.first); } return null; } Future<int> deleteNote(String images) async { var dbClient = await db; return await dbClient .delete(tableVideo, where: '$image = ?', whereArgs: [images]); } Future<int> updateNote(Video video) async { var dbClient = await db; return await dbClient.update(tableVideo, video.toJson(), where: "$columnId = ?", whereArgs: [video.id]); } Future close() async { var dbClient = await db; return dbClient.close(); }}
可以看到我们在执行相关方法的时候都会先获得db, db会执行一个initDb方法,用于创建数据库和表
initDb() async { String databasesPath = await getDatabasesPath(); String path = join(databasesPath, 'flashgo.db'); var db = await openDatabase(path, version: 1, onCreate: _onCreate); return db; } void _onCreate(Database db, int newVersion) async { await db.execute( 'CREATE TABLE $tableVideo($columnId INTEGER PRIMARY KEY, $image TEXT, $url TEXT, $duration INTEGER, $title TEXT, $favoriteStatus TEXT)'); }
- getDatabasesPath() : 获取默认数据库位置(在Android上,它通常是data/data/
/databases,在iOS上,它是Documents目录) - join(databasesPath, ‘flashgo.db’): 相当于在上述方法获取到的位置创建了一个名为flashgo的数据库.
- openDatabase: 按指定路径打开数据库 , 路径就是上面flash.db的路径,version为数据库版本号,onCreate是创建表的方法
然后是我定义的一个实体类,看一下代码:
class Video { int id; String image; String url; int duration; String title; bool favoriteStatus; Video( {this.id, this.image, this.url, this.duration, this.title, this.favoriteStatus}); Video.fromJson(Map json) { id = json['id']; image = json['image']; url = json['url']; duration = json['duration']; title = json['title']; favoriteStatus = json['favorite_status']; } Video.fromSql(Map json) { id = json['id']; image = json['image']; url = json['url']; duration = json['duration']; title = json['title']; favoriteStatus = json['favorite_status'] == 'true'; } Map toJson() { final Map data = new Map(); data['id'] = this.id; data['image'] = this.image; data['url'] = this.url; data['duration'] = this.duration; data['title'] = this.title; data['favorite_status'] = this.favoriteStatus; return data; }}
细心的同学可能看到了一个与众不同的方法: fromSql
,
其中的json['favorite_status']
是字符串类型, 为什么不依然用bool型呢, 因为sqlite不支持bool型.
上文中,CREATE TABLE $tableVideo($columnId INTEGER PRIMARY KEY, $image TEXT, $url TEXT, $duration INTEGER, $title TEXT, $favoriteStatus TEXT)
这个建表方法可以看到
id用的是integer, 其他都是用的text.
我们看一下sqlite都支持哪些数据类型吧:
sql存储数据类型
每个存储在 SQLite 数据库中的值都具有以下存储类之一:
存储类 | 描述 |
---|---|
NULL | 值是一个 NULL 值。 |
INTEGER | 值是一个带符号的整数,根据值的大小存储在 1、2、3、4、6 或 8 字节中。 |
REAL | 值是一个浮点值,存储为 8 字节的 IEEE 浮点数字。 |
TEXT | 值是一个文本字符串,使用数据库编码(UTF-8、UTF-16BE 或 UTF-16LE)存储。 |
BLOB | 值是一个 blob 数据,完全根据它的输入存储。 |
数据库和表已经准备就绪了,那么该看一看它的增删改查了
插入数据
Future insertVideo(Video video) async { var dbClient = await db; var result = await dbClient.insert(tableVideo, video.toJson()); return result; }
这里是我封装的一个插入数据的方法,参数是video的对象, 但是可以看到insert
方法第二个参数是一个json数据,所以其实也可以直接传递json数据.而不是传一个对象再转成json.
查询方法
Future selectVideos({int limit, int offset}) async { var dbClient = await db; var result = await dbClient.query( tableVideo, columns: [columnId, image, url, duration, title, favoriteStatus], limit: limit, offset: offset, ); List
主要是调用了query
方法,先看一下源码:
Future>> query(String table, {bool distinct, List columns, String where, List whereArgs, String groupBy, String having, String orderBy, int limit, int offset});
有一个必传参数是表名,
然后有很多修饰,
比如
limit : 是要查询多少条数据,
offset :是从哪里开始查.
columns: 是要查询哪几列
where: 是查询条件,这里我是查询所有的所以没有设置.
limit 和offset 这两个也是最常用的属性,所以我封装方法的时候允许设置这两个参数.
如果你有多张表,多个列需要查询,我建议各自封装方法,不然的话,需要传入的参数过于复杂便失去了封装的意义.
这里的查询方法返回的是json数据,且要记住,是只有integer和text类型的,所以想要bool一定要自己处理
List
所以这里,我新建了一个fromSql的方法,把查询出来的json数据转成我想要的类型的对象.
查询单个
Future
思路同上,只是要多了一个where,即查询条件,这里我是根据id来查 所以只传入了一个id参数.
返回查询到的结果(json类型) .
如果查询不到,则返回一个null.
更改数据
Future updateVideo(Video video) async { var dbClient = await db; return await dbClient.update(tableVideo, video.toJson(), where: "$columnId = ?", whereArgs: [video.id]); }
这个代码的逻辑是
- 传入更改后的数据
- 根据传入的数据id找到对应数据
- 更新数据
删除数据
Future deleteVideo(String images) async { var dbClient = await db; return await dbClient .delete(tableVideo, where: '$image = ?', whereArgs: [images]); }
其实逻辑和查询是一样的,我这是根据image来查找并删除.也可以用id或者其他数据.
获取数据的数量
Future getCount() async { var dbClient = await db; return Sqflite.firstIntValue( await dbClient.rawQuery('SELECT COUNT(*) FROM $tableVideo')); }
此方法用来查询表中有多少条数据.
这我用了rawQuery
方法, 它是支持直接使用sql语句进行查询的.
因为该结果返回一个列表,所以使用Sqflite.firstIntValue
来获取其中的第一个值.
关闭方法
Future close() async { var dbClient = await db; return dbClient.close(); }
在操作执行完毕后 , 记得关闭数据库.关闭之后无法再访问数据库.
以上是对代码的分析,下面看一下实际的使用:
//把视频列表存到数据库以备用 void saveVideos(List
相关代码尽在github
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- 一句话锁定MySQL数据占用元凶
- AndroidStudio 优秀的第三方数据库 GreenDao
- android在程序中打开另一个程序
- Android(安卓)百度地图使几点始终在合适的屏幕范围内显示
- Android(安卓)在PreferenceActivity 中移除一个Preference
- Android(安卓)5.0 如何正确启用isLoggable(二)__原理分析
- java做服务器,android做客户端,实现数据传输
- Android(安卓)基于DataBinding的通用RecyclerView Adapter