Flutter中SQFLite數據庫:
支持事務的批量操作
支持增刪改查
在iOS和Android雙端后臺線程中執行數據庫操作
使用方式:
1,添加依賴
dependencies:... sqflite: any
2,導入文件:
import 'package:sqflite/sqflite.dart';
3, //創建數據庫
Future<String> _createNewDb(String dbName) async {
//獲取數據庫文件路徑
var dbPath = await getDatabasesPath();
print('dbPath:' + dbPath);
String path = join(dbPath, dbName);
if (await new Directory(dirname(path)).exists()) {
await deleteDatabase(path);
} else {
try { await new Directory(dirname(path)).create(recursive: true);
} catch (e) { print(e);
}
}
return path;
}
_create() async {
dbPath = await _createNewDb(dbName);
Database db = await openDatabase(dbPath);
await db.execute(sql_createTable);
await db.close();
setState(() {
_result = '創建user.db成功,創建user_table成功'; });
}
//打開數據庫,獲取數據庫對象
_open() async {
if(null == dbPath){ var path = await getDatabasesPath();
dbPath = join(path, dbName); print('dbPath:' + dbPath);
}
return await openDatabase(dbPath);
}
4,刪除數據庫
await deleteDatabase(path);?
5,打開數據庫并創建一張表? ? ? ? ??
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)");
});? ?
// 插入數據庫的兩種方式
1,Future<int> rawInsert(String sql, [List<dynamic> arguments]);
2,Future<int> insert(String table, Map<String, dynamic> values,?{String nullColumnHack, ConflictAlgorithm conflictAlgorithm});
rawInsert方法第一個參數為一條插入sql語句,可以使用?作為占位符,通過第二個參數填充數據。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? insert方法第一個參數為操作的表名,第二個參數map中是想要添加的字段名和對應字段值。?
示例:
?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");?
//? 查詢操作的兩種方法
Future<List<Map<String, dynamic>>> query(String table,
?{bool distinct,
?List<String> columns,
?String where,
?List<dynamic> whereArgs,
?String groupBy,
?String having,
?String orderBy,
?intlimit,
?intoffset});
Future<List<Map<String, dynamic>>> rawQuery(String sql,?[List<dynamic> arguments]);?
query方法第一個參數為操作的表名,后邊的可選參數依次表示是否去重、查詢字段、WHERE子句(可使用?作為占位符)、WHERE子句占位符參數值、GROUP BY子句、HAVING子句、ORDER BY子句、查詢的條數、查詢的偏移位等。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? rawQuery方法第一個參數為一條查詢sql語句,可以使用?作為占位符,通過第二個參數填充數據。?
示例如下:
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");});?
//? 更新的兩種方式
Future<int> rawUpdate(String sql, [List<dynamic> arguments]);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Future<int> update(String table, Map<String, dynamic> values,?{String where,?List<dynamic> whereArgs,?ConflictAlgorithm conflictAlgorithm});
rawUpdate方法第一個參數為一條更新sql語句,可以使用?作為占位符,通過第二個參數填充數據。
update方法第一個參數為操作的表名,第二個參數為修改的字段和對應值,后邊的可選參數依次表示WHERE子句(可使用?作為占位符)、WHERE子句占位符參數值、發生沖突時的操作算法(包括回滾、終止、忽略等等)。
示例如下:
int count =awaitdatabase.rawUpdate(
UPDATE Test SET name = ?, VALUE = ? WHERE name = ?',["updated name","9876","some name"]);
print("updated: $count");
// 刪除的兩種方式
Future rawDelete(String sql, [List arguments]);?
Future delete(String table, {String where, List whereArgs});?
rawDelete方法第一個參數為一條刪除sql語句,可以使用?作為占位符,通過第二個參數填充數據。?
delete方法第一個參數為操作的表名,后邊的可選參數依次表示WHERE子句(可使用?作為占位符)、WHERE子句占位符參數值。?
示例如下:
count =awaitdatabase.rawDelete('DELETE FROM Test WHERE name = ?',['an
other name']);
assert(count==1);
// 獲取Test表的數據
List list = awaitdatabase.rawQuery('SELECT * FROM Test');List 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);
assert(constDeepCollectionEquality().equals(list, expectedList));
// 獲取記錄的數量
count = Sqflite.firstIntValue(awaitdatabase.rawQuery("SELECT COUNT(*) FROM Test"));
assert(count==2);
// 關閉數據庫
await? ?database.close();
Future close() async => db.close();
使用示例如下所示:
其中有一些注意事項:
1,Transcaction?
2,支持批量操作
但是這里注意,這些操作返回結果,都會有一些開銷,如果不考慮結果,可以使用。
事務期間,直到事務被提交,批量操作才能提交。
3,表和列的名字
通常避免使用SQLite關鍵字作為表名或者列名,例如以下其中之一。
如果必須使用如上,需要使用雙引號轉義,例如:
4,支持的列類型
首先,我們創建一個書籍類,包括書籍ID、書名、作者、價格、出版社等信息。
示例二:
finalString tableBook = 'book';finalString columnId = '_id';
finalString columnName = 'name';
finalString columnAuthor = 'author';
finalString columnPrice = 'price';
finalString columnPublishingHouse = 'publishingHouse';
classBook {
?intid;
?String name;
?String author;
?doubleprice;
?String publishingHouse;
?Map<String, dynamic> toMap() {
?var map = <String, dynamic>{
?columnName: name,
?columnAuthor: author,
?columnPrice: price,
?columnPublishingHouse: publishingHouse
?};
?if(id != null) {
?map[columnId] = id;
?}
?returnmap;
?}
?Book();
?Book.fromMap(Map<String, dynamic> map) {
?id = map[columnId];
?name = map[columnName];
?author = map[columnAuthor];
?price = map[columnPrice];
?publishingHouse = map[columnPublishingHouse];
?}
}
2,創建對應的數據庫文件和表
// 獲取數據庫文件的存儲路徑
?var databasesPath = await getDatabasesPath();
?String path = join(databasesPath, 'demo.db');
//根據數據庫文件路徑和數據庫版本號創建數據庫表
?db = await openDatabase(path, version: 1,
?onCreate: (Database db, intversion) async {
?await db.execute('''
??CREATE TABLE $tableBook (
??$columnId INTEGER PRIMARY KEY,
??$columnName TEXT,
??$columnAuthor TEXT,
??$columnPrice REAL,
??$columnPublishingHouse TEXT)
??''');
?});
3,CRUD實現
// 插入一條書籍數據Future<Book> insert(Book book) async {
book.id = await db.insert(tableBook, book.toMap());
returnbook;
}
// 查找所有書籍信息
Future<List<Book>> queryAll() async {
List<Map> maps = await db.query(tableBook, columns: [
columnId,
columnName,
columnAuthor,
columnPrice,
columnPublishingHouse
]);
if(maps == null|| maps.length == 0) {
returnnull;
}
List<Book> books = [];
for(inti = 0; i < maps.length; i++) {
books.add(Book.fromMap(maps[i]));
}
returnbooks;
}
// 根據ID查找書籍信息
Future<Book> getBook(intid) async {
List<Map> maps = await db.query(tableBook,
columns: [
?columnId,
?columnName,
?columnAuthor,
?columnPrice,
?columnPublishingHouse
],
where: '$columnId = ?',
whereArgs: [id]);
if(maps.length > 0) {
returnBook.fromMap(maps.first);
}
returnnull;
}
// 根據ID刪除書籍信息
Future<int> delete(intid) async {
returnawait db.delete(tableBook, where: '$columnId = ?', whereArgs: [id]);
}
// 更新書籍信息
Future<int> update(Book book) async {
returnawait db.update(tableBook, book.toMap(),
where: '$columnId = ?', whereArgs: [book.id]);
}
4,關閉數據庫,上文中已提示
5,CRUDUtils
_add() async {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Database db = await _open(); String sql = "INSERT INTO user_table(username,pwd) VALUES('$username','$pwd')";? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//開啟事務? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.transaction((txn) async {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int id = await txn.rawInsert(sql); });? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.close();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? setState(() {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _result = "插入username=$username,pwd=$pwd數據成功";? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }) ;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _delete() async {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Database db = await _open();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//刪除最近一條? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String sql = "DELETE FROM user_table where id in (select id from user_table order by id desc limit 1)";? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int count = await db.rawDelete(sql);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.close();????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? setState(() {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (count == 1) {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?_result = "刪除成功,請查看";???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? } else {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? _result = "刪除0條數據或刪除失敗,請看log";???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? } }); }???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?? _update() async {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? Database db = await _open();???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? String sql = "UPDATE user_table SET pwd = ? WHERE id = ?";???? ??? ??? ??? ??? ??? ??? ??? int count = await db.rawUpdate(sql, ["654321", '1']); print(count);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? await db.close();???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? setState(() {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?? _result = "更新數據成功,請查看";???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? }); }???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? //批量增、改、刪數據???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _batch() async {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Database db = await _open();???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? var batch = db.batch();???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? batch.insert("user_table", {"username": "batchName1"});???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? batch.update("user_table", {"username": "batchName2"}, where: "username = ?",whereArgs: ["batchName1"]);???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? batch.delete("user_table", where: "username = ?", whereArgs: ["Leon"]);? ? ? ? ? ? ? ? ? ?//返回每個數據庫操作的結果組成的數組 [6, 3, 0]:新增返回id=6,修改了3條數據,刪除了0條數據???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? var results = await batch.commit(); await db.close();???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? setState(() {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?? _result = "批量增、改數據成功 "+results.toString();???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? }); }???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?? _queryNum() async {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Database db = await _open(); int count = Sqflite.firstIntValue(???? ??? ??? ??? ??? ??? ??? ??? ??? await db.rawQuery(sql_query_count)); await db.close(); setState(() {???? ??? ??? ??? ??? ??? _result = "數據條數:$count"; });???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? }???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? _query() async {???? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?????? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? Database db = await _open(); List<Map> list = await db.rawQuery(sql_query);???? ??? await db.close(); setState(() { _result = "數據詳情:$list"; });? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }?
數據庫的創建、打開,都需要先獲取數據庫文件的路徑,獲取到path之后Database db = await openDatabase(dbPath);獲取數據庫對象,注意數據庫的操作全都是異步的,要用關鍵字await。