在 SQLite (Flutter) 中更新字段

Updating field in SQLite (Flutter)

让我们以此code snippet为例。数据模型非常简单:

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

  Dog({this.id, this.name, this.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],
  );
}

await updateDog(Dog(id: 0, name: 'Fido', age: 42));

它工作得很好,没有任何问题。现在的问题是,如何在不使用name的情况下更新only字段age?所以基本上我想做这样的事情

await updateDog(Dog(id: 0, age: 35));

并期待结果 "name: Figo, age: 35"。但相反,它删除了 null 中的 Fido。所以我得到了这个结果:"name: null, age: 35".

您可以复制粘贴 运行 下面的完整代码
示例代码有两条记录来演示更新效果
解决方案 1:您可以使用 rawUpdate
代码片段

int count = await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);

解决方案 2:您可以将 toMap 修改为仅 return idage

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

    // Update the given Dog.
    await db.update(
      'dogs',
      dog.toMap1(),
 ...
Map<String, dynamic> toMap1() {
    return {
      'id': id,
      'age': age,
    };
  }

fido = Dog(
    id: fido.id,
    name: "not care",
    age: 35,
  );
 await updateDog1(fido);

输出

I/flutter ( 6570): [Dog{id: 0, name: Fido, age: 42}, Dog{id: 1, name: abc, age: 10}]
I/flutter ( 6570): updated: 1
I/flutter ( 6570): [Dog{id: 0, name: Fido, age: 35}, Dog{id: 1, name: abc, age: 10}]

完整代码解决方案1

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();
  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) {
      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,
  );

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

    // Insert the Dog into the correct table. Also specify the
    // `conflictAlgorithm`. In this case, if the same dog is inserted
    // multiple times, it replaces the previous data.
    await db.insert(
      'dogs',
      dog.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  Future<List<Dog>> dogs() async {
    // Get a reference to the database.
    final Database db = await database;

    // Query the table for all The Dogs.
    final List<Map<String, dynamic>> maps = await db.query('dogs');

    // Convert the List<Map<String, dynamic> into a List<Dog>.
    return List.generate(maps.length, (i) {
      return Dog(
        id: maps[i]['id'],
        name: maps[i]['name'],
        age: maps[i]['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],
    );
  }

  var fido = Dog(
    id: 0,
    name: 'Fido',
    age: 42,
  );

  var fido1 = Dog(
    id: 1,
    name: 'abc',
    age: 10,
  );

  // Insert a dog into the database.
  await insertDog(fido);
  await insertDog(fido1);

  // Print the list of dogs (only Fido for now).
  print(await dogs());
/*
  // 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 Fido's updated information.
  print(await dogs());*/

  final Database db = await database;
  int count =
      await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);
  print('updated: $count');
  print(await dogs());

  /*// 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({this.id, this.name, this.age});

  Map<String, dynamic> 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}';
  }
}

完整代码解决方案2

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();
  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) {
      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,
  );

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

    // Insert the Dog into the correct table. Also specify the
    // `conflictAlgorithm`. In this case, if the same dog is inserted
    // multiple times, it replaces the previous data.
    await db.insert(
      'dogs',
      dog.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }

  Future<List<Dog>> dogs() async {
    // Get a reference to the database.
    final Database db = await database;

    // Query the table for all The Dogs.
    final List<Map<String, dynamic>> maps = await db.query('dogs');

    // Convert the List<Map<String, dynamic> into a List<Dog>.
    return List.generate(maps.length, (i) {
      return Dog(
        id: maps[i]['id'],
        name: maps[i]['name'],
        age: maps[i]['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> updateDog1(Dog dog) async {
    // Get a reference to the database.
    final db = await database;

    // Update the given Dog.
    await db.update(
      'dogs',
      dog.toMap1(),
      // 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],
    );
  }

  var fido = Dog(
    id: 0,
    name: 'Fido',
    age: 42,
  );

  var fido1 = Dog(
    id: 1,
    name: 'abc',
    age: 10,
  );

  // Insert a dog into the database.
  await insertDog(fido);
  await insertDog(fido1);

  // Print the list of dogs (only Fido for now).
  print(await dogs());

  // Update Fido's age and save it to the database.
  fido = Dog(
    id: fido.id,
    name: "not care",
    age: 35,
  );
  await updateDog1(fido);

  // Print Fido's updated information.
  print(await dogs());

  /*final Database db = await database;
  int count =
      await db.rawUpdate('UPDATE dogs SET age = ? WHERE id = ?', [35, 0]);
  print('updated: $count');
  print(await dogs());*/

  /*// 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({this.id, this.name, this.age});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
      'age': age,
    };
  }

  Map<String, dynamic> toMap1() {
    return {
      'id': id,
      '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}';
  }
}

文档中的示例如下所示:

// 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);

您要么像 chunhunghan 的回答那样使用原始 SQL 查询来处理它,要么 使用 id 查询狗,然后覆盖字段,然后更新。

为什么?

让我们看看您的更新代码:

await updateDog(Dog(id: 0, age: 35));

调用更新行时,将调用 Dog.toMap(),看起来您正在将名称更新为 null

你可以在这里做你想做的是代码:

 Future<Dog> getDog(int id) async {
    List<Map> result = await database.query(..... whereArgs: [id]);
    if (result.length > 0) {
      return new Dog.fromMap(result.first);
    }
    return null;
 }

// Now in code
fido = await getDog(id);
// Update Fido's age and save it to the database.
fido = Dog(
 id: fido.id,
 name: fido.name,
 age: 35, //<--
);
await updateDog(fido);