跳至主要內容

使用 SQLite 持久化資料

如果您正在撰寫需要在本機裝置上持久保存和查詢大量資料的應用程式,請考慮使用資料庫而不是本機檔案或鍵值儲存。一般而言,與其他本機持久性解決方案相比,資料庫提供更快的插入、更新和查詢速度。

Flutter 應用程式可以透過 pub.dev 上提供的 sqflite 外掛程式來使用 SQLite 資料庫。這個範例示範了使用 sqflite 來插入、讀取、更新和移除關於各種 Dog 的資料的基本方法。

如果您是 SQLite 和 SQL 陳述式的新手,請在完成這個範例之前,先複習 SQLite 教學,以了解基本知識。

這個範例使用以下步驟

  1. 新增相依性。
  2. 定義 Dog 資料模型。
  3. 開啟資料庫。
  4. 建立 dogs 資料表。
  5. Dog 插入資料庫。
  6. 擷取 dogs 清單。
  7. 更新資料庫中的 Dog
  8. 從資料庫刪除 Dog

1. 新增相依性

#

若要使用 SQLite 資料庫,請匯入 sqflitepath 套件。

  • sqflite 套件提供類別和函式,用於與 SQLite 資料庫互動。
  • path 套件提供函式,用於定義將資料庫儲存在磁碟上的位置。

若要將這些套件新增為相依性,請執行 flutter pub add

flutter pub add sqflite path

請務必在您將使用的檔案中匯入這些套件。

dart
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

2. 定義 Dog 資料模型

#

在建立資料表以儲存 Dog 的資訊之前,請花一些時間定義需要儲存的資料。在此範例中,定義一個 Dog 類別,其中包含三筆資料:每個狗的唯一 idnameage

dart
class Dog {
  final int id;
  final String name;
  final int age;

  const Dog({
    required this.id,
    required this.name,
    required this.age,
  });
}

3. 開啟資料庫

#

在讀取和寫入資料庫之前,請先開啟與資料庫的連線。這包含兩個步驟

  1. 使用 sqflite 套件中的 getDatabasesPath(),結合 path 套件中的 join 函式,定義資料庫檔案的路徑。
  2. 使用 sqflite 中的 openDatabase() 函式開啟資料庫。
dart
// Avoid errors caused by flutter upgrade.
// Importing 'package:flutter/widgets.dart' is required.
WidgetsFlutterBinding.ensureInitialized();
// Open the database and store the reference.
final database = openDatabase(
  // Set the path to the database. Note: Using the `join` function from the
  // `path` package is best practice to ensure the path is correctly
  // constructed for each platform.
  join(await getDatabasesPath(), 'doggie_database.db'),
);

4. 建立 dogs 資料表

#

接下來,建立一個資料表來儲存各種 Dog 的資訊。在此範例中,建立一個名為 dogs 的資料表,定義可以儲存的資料。每個 Dog 都包含 idnameage。因此,這些在 dogs 資料表中表示為三個欄位。

  1. id 是一個 Dart int,並儲存為 INTEGER SQLite 資料類型。將 id 作為資料表的主鍵也是一個好習慣,可以縮短查詢和更新時間。
  2. name 是一個 Dart String,並儲存為 TEXT SQLite 資料類型。
  3. age 也是一個 Dart int,並儲存為 INTEGER 資料類型。

如需更多關於可以在 SQLite 資料庫中儲存的可用資料類型的資訊,請參閱 官方 SQLite 資料類型文件

dart
final database = openDatabase(
  // Set the path to the database. Note: Using the `join` function from the
  // `path` package is best practice to ensure the path is correctly
  // constructed for each platform.
  join(await getDatabasesPath(), 'doggie_database.db'),
  // When the database is first created, create a table to store dogs.
  onCreate: (db, version) {
    // Run the CREATE TABLE statement on the database.
    return db.execute(
      'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
    );
  },
  // Set the version. This executes the onCreate function and provides a
  // path to perform database upgrades and downgrades.
  version: 1,
);

5. 將 Dog 插入資料庫

#

現在您有了一個資料庫,其中包含一個適合儲存各種狗的資訊的資料表,現在是讀取和寫入資料的時候了。

首先,將 Dog 插入 dogs 資料表。這包含兩個步驟

  1. Dog 轉換為 Map
  2. 使用 insert() 方法將 Map 儲存在 dogs 資料表中。
dart
class Dog {
  final int id;
  final String name;
  final int age;

  Dog({
    required this.id,
    required this.name,
    required this.age,
  });

  // Convert a Dog into a Map. The keys must correspond to the names of the
  // columns in the database.
  Map<String, Object?> toMap() {
    return {
      'id': id,
      'name': name,
      'age': age,
    };
  }

  // Implement toString to make it easier to see information about
  // each dog when using the print statement.
  @override
  String toString() {
    return 'Dog{id: $id, name: $name, age: $age}';
  }
}
dart
// Define a function that inserts dogs into the database
Future<void> insertDog(Dog dog) async {
  // Get a reference to the database.
  final db = await database;

  // Insert the Dog into the correct table. You might also specify the
  // `conflictAlgorithm` to use in case the same dog is inserted twice.
  //
  // In this case, replace any previous data.
  await db.insert(
    'dogs',
    dog.toMap(),
    conflictAlgorithm: ConflictAlgorithm.replace,
  );
}
dart
// Create a Dog and add it to the dogs table
var fido = Dog(
  id: 0,
  name: 'Fido',
  age: 35,
);

await insertDog(fido);

6. 擷取 Dog 清單

#

現在 Dog 已儲存在資料庫中,請查詢資料庫以取得特定的狗或所有狗的清單。這包含兩個步驟

  1. 針對 dogs 資料表執行 query。這會傳回 List<Map>
  2. List<Map> 轉換為 List<Dog>
dart
// A method that retrieves all the dogs from the dogs table.
Future<List<Dog>> dogs() async {
  // Get a reference to the database.
  final db = await database;

  // Query the table for all the dogs.
  final List<Map<String, Object?>> dogMaps = await db.query('dogs');

  // Convert the list of each dog's fields into a list of `Dog` objects.
  return [
    for (final {
          'id': id as int,
          'name': name as String,
          'age': age as int,
        } in dogMaps)
      Dog(id: id, name: name, age: age),
  ];
}
dart
// Now, use the method above to retrieve all the dogs.
print(await dogs()); // Prints a list that include Fido.

7. 更新資料庫中的 Dog

#

將資訊插入資料庫後,您可能希望稍後更新該資訊。您可以使用 sqflite 程式庫中的 update() 方法來執行此操作。

這包含兩個步驟

  1. 將 Dog 轉換為 Map。
  2. 使用 where 子句來確保您更新的是正確的 Dog。
dart
Future<void> updateDog(Dog dog) async {
  // Get a reference to the database.
  final db = await database;

  // Update the given Dog.
  await db.update(
    'dogs',
    dog.toMap(),
    // Ensure that the Dog has a matching id.
    where: 'id = ?',
    // Pass the Dog's id as a whereArg to prevent SQL injection.
    whereArgs: [dog.id],
  );
}
dart
// Update Fido's age and save it to the database.
fido = Dog(
  id: fido.id,
  name: fido.name,
  age: fido.age + 7,
);
await updateDog(fido);

// Print the updated results.
print(await dogs()); // Prints Fido with age 42.

8. 從資料庫刪除 Dog

#

除了插入和更新關於 Dog 的資訊之外,您也可以從資料庫中移除 Dog。若要刪除資料,請使用 sqflite 程式庫中的 delete() 方法。

在本節中,建立一個函式,該函式會接受 id 並從資料庫中刪除具有相符 id 的狗。為了讓它正常運作,您必須提供 where 子句來限制要刪除的記錄。

dart
Future<void> deleteDog(int id) async {
  // Get a reference to the database.
  final db = await database;

  // Remove the Dog from the database.
  await db.delete(
    'dogs',
    // Use a `where` clause to delete a specific dog.
    where: 'id = ?',
    // Pass the Dog's id as a whereArg to prevent SQL injection.
    whereArgs: [id],
  );
}

範例

#

若要執行此範例

  1. 建立一個新的 Flutter 專案。
  2. sqflitepath 套件新增至您的 pubspec.yaml
  3. 將以下程式碼貼到名為 lib/db_test.dart 的新檔案中。
  4. 使用 flutter run lib/db_test.dart 執行程式碼。
dart
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

void main() async {
  // Avoid errors caused by flutter upgrade.
  // Importing 'package:flutter/widgets.dart' is required.
  WidgetsFlutterBinding.ensureInitialized();
  // Open the database and store the reference.
  final database = openDatabase(
    // Set the path to the database. Note: Using the `join` function from the
    // `path` package is best practice to ensure the path is correctly
    // constructed for each platform.
    join(await getDatabasesPath(), 'doggie_database.db'),
    // When the database is first created, create a table to store dogs.
    onCreate: (db, version) {
      // Run the CREATE TABLE statement on the database.
      return db.execute(
        'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
      );
    },
    // Set the version. This executes the onCreate function and provides a
    // path to perform database upgrades and downgrades.
    version: 1,
  );

  // Define a function that inserts dogs into the database
  Future<void> insertDog(Dog dog) async {
    // Get a reference to the database.
    final db = await database;

    // Insert the Dog into the correct table. You might also specify the
    // `conflictAlgorithm` to use in case the same dog is inserted twice.
    //
    // In this case, replace any previous data.
    await db.insert(
      'dogs',
      dog.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  // A method that retrieves all the dogs from the dogs table.
  Future<List<Dog>> dogs() async {
    // Get a reference to the database.
    final db = await database;

    // Query the table for all the dogs.
    final List<Map<String, Object?>> dogMaps = await db.query('dogs');

    // Convert the list of each dog's fields into a list of `Dog` objects.
    return [
      for (final {
            'id': id as int,
            'name': name as String,
            'age': age as int,
          } in dogMaps)
        Dog(id: id, name: name, age: age),
    ];
  }

  Future<void> updateDog(Dog dog) async {
    // Get a reference to the database.
    final db = await database;

    // Update the given Dog.
    await db.update(
      'dogs',
      dog.toMap(),
      // Ensure that the Dog has a matching id.
      where: 'id = ?',
      // Pass the Dog's id as a whereArg to prevent SQL injection.
      whereArgs: [dog.id],
    );
  }

  Future<void> deleteDog(int id) async {
    // Get a reference to the database.
    final db = await database;

    // Remove the Dog from the database.
    await db.delete(
      'dogs',
      // Use a `where` clause to delete a specific dog.
      where: 'id = ?',
      // Pass the Dog's id as a whereArg to prevent SQL injection.
      whereArgs: [id],
    );
  }

  // Create a Dog and add it to the dogs table
  var fido = Dog(
    id: 0,
    name: 'Fido',
    age: 35,
  );

  await insertDog(fido);

  // Now, use the method above to retrieve all the dogs.
  print(await dogs()); // Prints a list that include Fido.

  // Update Fido's age and save it to the database.
  fido = Dog(
    id: fido.id,
    name: fido.name,
    age: fido.age + 7,
  );
  await updateDog(fido);

  // Print the updated results.
  print(await dogs()); // Prints Fido with age 42.

  // Delete Fido from the database.
  await deleteDog(fido.id);

  // Print the list of dogs (empty).
  print(await dogs());
}

class Dog {
  final int id;
  final String name;
  final int age;

  Dog({
    required this.id,
    required this.name,
    required this.age,
  });

  // Convert a Dog into a Map. The keys must correspond to the names of the
  // columns in the database.
  Map<String, Object?> toMap() {
    return {
      'id': id,
      'name': name,
      'age': age,
    };
  }

  // Implement toString to make it easier to see information about
  // each dog when using the print statement.
  @override
  String toString() {
    return 'Dog{id: $id, name: $name, age: $age}';
  }
}