前言
做过手机端应用开发的人,都应该知道SQLite数据库,它是手机端上最老牌,最流行的数据库。使用起来比shared_preferences稍微复杂一点。
前面一篇博文介绍的shared_preferences,我们可以理解为key-value的存储模式,在需要对大量数据进行增删改查操作时,我们就必须使用数据库。比如,在新闻App中,为了用户更好的体验,我们需要存储一些新闻数据,方便用户在离线的模式下,也能体验App的基本功能。
所以为了更好的操作SQLite数据库,Flutter为我们提供了sqflite插件进行数据库的操作,下面我们来详细讲解SQLite的操作方式。
sqflite依赖库
目前与数据库相关,并且使用最广的依赖库就是sqflite。它同时支持Android和IOS两个版本。我们可以直接在Flutter Packages上搜索sqflite,找到下载地址和使用说明。sqflite有以下特性:
(1)支持事务和批处理
(2)支持自动Version管理
(3)支持增删改查的Helpers工具类
(4)支持Android/iOS后台线程的运行
既然我们之前说过sqflite是插件,所以我们开发的时候,需要在Flutter项目中引入这个插件,代码如下:
dependencies: flutter: sdk: flutter shared_preferences: ^0.5.1+1 sqflite: ^1.1.3//引入代码
sqflite基本用法
sqflite常用的操作方式有8种,代码如下所示:
获取和删除database
_getOrdelete_database()async{ var databasesPath=await getDatabasesPath(); String path=join(databasesPath,'demo.db'); await deleteDatabase(path); }
创建数据库与数据表
_create_database() async{ 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 Demo(id INTEGER PRIMARY KEY,name TEXT,value INTEGER,num REAL)'); }); }
插入数据
第一种插入数据方法的定义,代码如下:
Future<int> rawInsert(String sql,[List<dynamic> arguments]);
从参数的名称我们可以看出来,第一个参数是一个字符串的sql语句,第二个参数就是传递的List类型的参数,当然前面sql语句需要使用“?”作为占位符,才能传递第二个参数,具体使用代码如下:
_insert_database() async{ 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 Demo(id INTEGER PRIMARY KEY,name TEXT,age INTEGER)'); }); await database.transaction((txn) async { int id1 = await txn.rawInsert( 'INSERT INTO Demo(name, value, num) VALUES(?, ?)',['Liyuanjing',27]); print('inserted1: $id1'); }); }
这种是在直接使用sql语句的情况下插入数据,而且参数必须与前面占位符个数一致。
第二种方法在直接使用表名进行插入:
Future<int> insert(String table,Map<String,dynamic> values,{String nullColumnHack,ConflictAlgorithm conflictAlgorithm});
这个方法第一个参数是需要操作的表明,第二个参数是需要插入的数据,通过字段名与对应的值匹配插入,使用代码如下:
_insert_database() async{ 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 Demo(id INTEGER PRIMARY KEY,name TEXT,age INTEGER)'); }); Map<String,dynamic> values={ 'name':'Liyuanjing', 'age': 27, }; await database.insert('Demo', values); }
修改数据
修改操作同样有两种操作方式,第一种方法定义的代码如下:
Future<int> rawUpdate(String sql,[List<dynamic> arguments]);
使用的详细代码如下:
_update_table() async{ 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 Demo(id INTEGER PRIMARY KEY,name TEXT,age INTEGER)'); }); await database.transaction((txn) async { int id1 = await txn.rawUpdate( 'UPDATE Demo SET name=? WHERE age=?',["Batman",27]); print('inserted1: $id1'); }); }
第二种方法的代码如下:
Future<int> update(String table,Map<String,dynamic> values,{String where,List<dynamic> whereArgs,ConflictAlgorithm conflictAlgorithm})
使用的代码如下,跟(3)中的第二种方法类似:
_update_table() async{ 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 Demo(id INTEGER PRIMARY KEY,name TEXT,age INTEGER)'); }); Map<String,dynamic> values={ 'name':'Batman', }; await database.update('Demo', values,where: 'age=?',whereArgs: [27,]); }
查询数据
同样,查询在sqflite插件中,也有两种操作方式,第一种的方法定义如下:
Future<List<Map<String,dynamic>>> rawQuery(String sql,[List<dynamic> arguments]);
rawQuery方法的第一个参数为查询用的sql语句,同样用占位符代替参数,第二个参数传入List类型参数来填充sql语句的占位符,占位符与List个数一致,使用代码如下:
_select_database() async{ 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 Demo(id INTEGER PRIMARY KEY,name TEXT,age INTEGER)'); }); await database.transaction((txn) async { List<Map<String,dynamic>> id1 = await txn.rawQuery( 'SELECT * FROM Demo WHERE age=?',[27]); }); }
接着我们在来看看第二种查询方法的定义,代码如下:
Future<List<Map<String,dynamic>>> query(String table, {bool distinct, List<String> columns, String where, List<dynamic> whereArgs, String groupBy, String having, String orderBy, int limit, int offset});
query方法的第一个参数是操作表名,后边的可选参数依次表示是否去重,查询字段,where的查询子句,where子句的占位符参数值,分组查询子句(groupBy),having子句,orderBy排序子句,limit查询上限条数,查询的偏移位(offset)。基本的使用代码如下:
_select_database() async{ 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 Demo(id INTEGER PRIMARY KEY,name TEXT,age INTEGER)'); }); await database.query('Demo',where: 'age=?',whereArgs: [27,]); }
删除数据
删除数据也分为两种分别是物理删除和逻辑删除。物理删除指的是操作真实的数据库并实现删除操作,而逻辑删除指的是通过update语句修改数据库,并且在查询时过滤掉不符合查询条件的记录,下面我们看一下sqflite里怎样实现逻辑删除。两种操作方式的方法定义代码如下:
Future<int> rawDelete(String sql,[List<dynamic arguments>]); Future<int> delete(String table,{String where,List<dynamic> whereArgs});
这两种操作方式与上面的增改差基本一致,这里就不在用代码赘述了,除了sql语句不同,其他的一摸一样。
计算总记录数
我们直接看代码:
Sqflite.firstIntValue(await database.rawQuery('Select COUNT(*) FROM Demo'));
关闭数据库
代码很简单,我们这里也直接上代码:
await database.close();
以上八个操作是sqflite的基本操作,除了增删改查稍微复杂一点之外,其他的基本一眼就能掌握,所以本篇终点在于增删改查。