Flutter持久化儲存之資料庫儲存

Flutter程式設計指南發表於2019-03-08

前言

資料庫儲存是我們常用的儲存方式之一,對大批量資料有增、刪、改、查操作需求時,我們就會想到使用資料庫,Flutter中提供了一個sqflite外掛供我們用於大量資料執行CRUD操作。本篇我們就來一起學習sqflite的使用。

sqflite使用

引入外掛

在pubspec.yaml檔案中新增path_provider外掛,最新版本為1.0.0,如下:

dependencies:
  flutter:
    sdk: flutter
  #sqflite外掛
  sqflite: 1.0.0
複製程式碼

然後命令列執行flutter packages get即可將外掛下載到本地。

資料庫操作方法介紹

1. 插入操作

插入資料操作有兩個方法:

Future<int> rawInsert(String sql, [List<dynamic> arguments]);

Future<int> insert(String table, Map<String, dynamic> values,
      {String nullColumnHack, ConflictAlgorithm conflictAlgorithm});
複製程式碼

rawInsert方法第一個引數為一條插入sql語句,可以使用?作為佔位符,通過第二個引數填充資料。

insert方法第一個引數為操作的表名,第二個引數map中是想要新增的欄位名和對應欄位值。

2. 查詢操作

查詢操作同樣實現了兩個方法:

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});
      
Future<List<Map<String, dynamic>>> rawQuery(String sql,
      [List<dynamic> arguments]);
複製程式碼

query方法第一個引數為操作的表名,後邊的可選引數依次表示是否去重、查詢欄位、WHERE子句(可使用?作為佔位符)、WHERE子句佔位符引數值、GROUP BY子句、HAVING子句、ORDER BY子句、查詢的條數、查詢的偏移位等。

rawQuery方法第一個引數為一條查詢sql語句,可以使用?作為佔位符,通過第二個引數填充資料。

3. 修改操作

修改操作同樣實現了兩個方法:

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子句佔位符引數值、發生衝突時的操作演算法(包括回滾、終止、忽略等等)。

4. 刪除操作

修改操作同樣實現了兩個方法:

Future<int> rawDelete(String sql, [List<dynamic> arguments]);

Future<int> delete(String table, {String where, List<dynamic> whereArgs});
複製程式碼

rawDelete方法第一個引數為一條刪除sql語句,可以使用?作為佔位符,通過第二個引數填充資料。

delete方法第一個引數為操作的表名,後邊的可選引數依次表示WHERE子句(可使用?作為佔位符)、WHERE子句佔位符引數值。

舉個栗子

我們以圖書管理系統來舉例。

首先,我們建立一個書籍類,包括書籍ID、書名、作者、價格、出版社等資訊。

final String tableBook = 'book';
final String columnId = '_id';
final String columnName = 'name';
final String columnAuthor = 'author';
final String columnPrice = 'price';
final String columnPublishingHouse = 'publishingHouse';

class Book {
  int id;
  String name;
  String author;
  double price;
  String publishingHouse;
  
  Map<String, dynamic> toMap() {
    var map = <String, dynamic>{
      columnName: name,
      columnAuthor: author,
      columnPrice: price,
      columnPublishingHouse: publishingHouse
    };
    if (id != null) {
      map[columnId] = id;
    }
    return map;
  }

  Book();

  Book.fromMap(Map<String, dynamic> map) {
    id = map[columnId];
    name = map[columnName];
    author = map[columnAuthor];
    price = map[columnPrice];
    publishingHouse = map[columnPublishingHouse];
  }
}
複製程式碼

其次,我們開始實現資料庫相關操作:

1. 建立資料庫檔案和對應的表
// 獲取資料庫檔案的儲存路徑
    var databasesPath = await getDatabasesPath();
    String path = join(databasesPath, 'demo.db');

//根據資料庫檔案路徑和資料庫版本號建立資料庫表
    db = await openDatabase(path, version: 1,
        onCreate: (Database db, int version) async {
      await db.execute('''
          CREATE TABLE $tableBook (
            $columnId INTEGER PRIMARY KEY, 
            $columnName TEXT, 
            $columnAuthor TEXT, 
            $columnPrice REAL, 
            $columnPublishingHouse TEXT)
          ''');
    });
複製程式碼
2. CRUD操作實現
  // 插入一條書籍資料
  Future<Book> insert(Book book) async {
    book.id = await db.insert(tableBook, book.toMap());
    return book;
  }

  // 查詢所有書籍資訊
  Future<List<Book>> queryAll() async {
    List<Map> maps = await db.query(tableBook, columns: [
      columnId,
      columnName,
      columnAuthor,
      columnPrice,
      columnPublishingHouse
    ]);

    if (maps == null || maps.length == 0) {
      return null;
    }

    List<Book> books = [];
    for (int i = 0; i < maps.length; i++) {
      books.add(Book.fromMap(maps[i]));
    }

    return books;
  }

  // 根據ID查詢書籍資訊
  Future<Book> getBook(int id) async {
    List<Map> maps = await db.query(tableBook,
        columns: [
          columnId,
          columnName,
          columnAuthor,
          columnPrice,
          columnPublishingHouse
        ],
        where: '$columnId = ?',
        whereArgs: [id]);
    if (maps.length > 0) {
      return Book.fromMap(maps.first);
    }
    return null;
  }

  // 根據ID刪除書籍資訊
  Future<int> delete(int id) async {
    return await db.delete(tableBook, where: '$columnId = ?', whereArgs: [id]);
  }

  // 更新書籍資訊
  Future<int> update(Book book) async {
    return await db.update(tableBook, book.toMap(),
        where: '$columnId = ?', whereArgs: [book.id]);
  }
複製程式碼
3. 關閉資料庫

資料庫物件使用完之後要在適當的時候關閉掉,可在helper類中實現以下方法。

Future close() async => db.close();
複製程式碼

執行效果:

事務

sqflite同時支援事務,通過事務可以將多條原子操作放在一起執行,保證操作要麼全部執行完成,要麼都不執行。 比如有兩條書籍資料必須全部插入書庫中才算新增成功,則使用如下方法

  Future<bool> insertTwoBook(Book book1, Book book2) async {
    return await db.transaction((Transaction txn) async {
      book1.id = await db.insert(tableBook, book1.toMap());

      book2.id = await db.insert(tableBook, book2.toMap());

      print('book1.id = ${book1.id}, book2.id = ${book2.id}');
      return book1.id != null && book2.id != null;
    });
  }
複製程式碼

寫在最後

以上介紹了sqflite中我們常用的幾個操作,有了sqflite我們就可以開發更豐富的應用程式,在開發實踐中大家遇到任何問題都可以給我們發訊息反饋,大家一起交流探討共同進步。針對一些使用者的反饋我們將在下一篇介紹Flutter的程式碼除錯。

說明:

文章轉載自對應的“Flutter程式設計指南”微信公眾號,更多Flutter相關技術文章開啟微信掃描二維碼關注微信公眾號獲取。

Flutter持久化儲存之資料庫儲存

相關文章