虽有智慧,不如乘势;虽有镃基,不如待时。
做过手机端应用开发的人,都应该知道SQLite数据库,它是手机端上最老牌,最流行的数据库。使用起来比shared_preferences稍微复杂一点。
前面一篇博文介绍的shared_preferences,我们可以理解为key-value的存储模式,在需要对大量数据进行增删改查操作时,我们就必须使用数据库。比如,在新闻App中,为了用户更好的体验,我们需要存储一些新闻数据,方便用户在离线的模式下,也能体验App的基本功能。
所以为了更好的操作SQLite数据库,Flutter为我们提供了sqflite插件进行数据库的操作,下面我们来详细讲解SQLite的操作方式。
目前与数据库相关,并且使用最广的依赖库就是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常用的操作方式有8种,代码如下所示:
_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的基本操作,除了增删改查稍微复杂一点之外,其他的基本一眼就能掌握,所以本篇终点在于增删改查。