【问题标题】:Failing to update value sembast database Flutter无法更新值 sembast 数据库 Flutter
【发布时间】:2020-08-29 13:51:14
【问题描述】:

我正在尝试使用 sambas 数据库,因为我需要将它用于我的应用程序的 Web 版本,但我正在努力解决它。 我的应用程序中有一个OpeningTimes 表单,我将它作为地图保存到数据库中。 我在保存方法中使用的逻辑是:使用我传入的OpeningTimes 对象的userName 参数从db 加载表单,如果没有找到则保存它,如果找到记录则使用传入对象。我正在使用 flutter_bloc 来管理它,所以在OpeningTimesBloc 中,当表单保存到数据库时,它会加载它并生成它,以便将 UI 更新到上次保存的状态。问题是更新后从db加载时,加载的值不是update方法中传入的值(例如monMorAct:要从true更新为false):

flutter: updateOpeningTimes() : update opening time received {userName: zazza zenigata, monMorOp: 10:00, monMorCl: 19:00, monMorAct: false, monAftOp: , monAftCl: , monAftAct: false, tueMorOp: , tueMorCl: , tueMorAct: false, tueAftOp: , tueAftCl: , tueAftAct: false, wedMorOp: , wedMorCl: , wedMorAct: false, wedAftOp: , wedAftCl: , wedAftAct: false, thuMorOp: , thuMorCl: , thuMorAct: false, thuAftOp: , thuAftCl: , thuAftAct: false, friMorOp: , friMorCl: , friMorAct: false, friAftOp: , friAftCl: , friAftAct: false, satMorOp: , satMorCl: , satMorAct: false, satAftOp: , satAftCl: , satAftAct: false, sunMorOp: , sunMorCl: , sunMorAct: false, sunAftOp: , sunAftCl: , sunAftAct: false}

但在加载时:

flutter: loadOpeningTimes() snapshot is: Record(openingTimeStorage, 1) {userName: zazza zenigata, monMorOp: 10:00, monMorCl: 19:00, monMorAct: true, monAftOp: , monAftCl: , monAftAct: false, tueMorOp: , tueMorCl: , tueMorAct: false, tueAftOp: , tueAftCl: , tueAftAct: false, wedMorOp: , wedMorCl: , wedMorAct: false, wedAftOp: , wedAftCl: , wedAftAct: false, thuMorOp: , thuMorCl: , thuMorAct: false, thuAftOp: , thuAftCl: , thuAftAct: false, friMorOp: , friMorCl: , friMorAct: false, friAftOp: , friAftCl: , friAftAct: false, satMorOp: , satMorCl: , satMorAct: false, satAftOp: , satAftCl: , satAftAct: false, sunMorOp: , sunMorCl: , sunMorAct: false, sunAftOp: , sunAftCl: , sunAftAct: false}

你能发现我做错了什么吗? 一如既往,非常感谢您的时间和帮助。

这是单例:

import 'dart:async';

import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sembast/sembast.dart';
import 'package:sembast/sembast_io.dart';

class AppDatabase {
  // Singleton instance
  static final AppDatabase _singleton = AppDatabase._();

  // Singleton accessor
  static AppDatabase get instance => _singleton;

  // Completer is used for transforming synchronous code into asynchronous code.
  Completer<Database> _dbOpenCompleter;

  // A private constructor. Allows us to create instances of AppDatabase
  // only from within the AppDatabase class itself.
  AppDatabase._();

  // Sembast database object
  Database _database;

  // Database object accessor
  Future<Database> get database async {
    // If completer is null, AppDatabaseClass is newly instantiated, so database is not yet opened
    if (_dbOpenCompleter == null) {
      _dbOpenCompleter = Completer();
      // Calling _openDatabase will also complete the completer with database instance
      _openDatabase();
    }
    // If the database is already opened, awaiting the future will happen instantly.
    // Otherwise, awaiting the returned future will take some time - until complete() is called
    // on the Completer in _openDatabase() below.
    return _dbOpenCompleter.future;
  }

  Future _openDatabase() async {
    // Get a platform-specific directory where persistent app data can be stored
    final appDocumentDir = await getApplicationDocumentsDirectory();
    // Path with the form: /platform-specific-directory/demo.db
    final dbPath = join(appDocumentDir.path, 'demo.db');

    final database = await databaseFactoryIo.openDatabase(dbPath);
    // Any code awaiting the Completer's future will now start executing
    _dbOpenCompleter.complete(database);
  }
}

这是集团:

class OpeningTimesBloc extends Bloc<OpeningTimesEvent, OpeningTimesState> {
  OpeningTimesRepository _openingTimesRepository = OpeningTimesRepository();
  @override
  OpeningTimesState get initialState => InitialState();

  @override
  Stream<OpeningTimesState> mapEventToState(OpeningTimesEvent event) async* {
    if (event is LoadOpeningTimes) {
      print('GetOpeningTimes event received');
      yield* _mapLoadOpeningTimesToState(event);
    }
    if (event is SaveOpeningTimes) {
      yield* _mapSaveOpeningTimesToState(event);
    }
  }

  Stream<OpeningTimesState> _mapLoadOpeningTimesToState(
      LoadOpeningTimes event) async* {
    OpeningTimes openingTimes = await _openingTimesRepository.loadOpeningTimes(
        userName: event.user.name);
    print(
        '_mapLoadOpeningTimesToState() loaded openingTimes is: ${openingTimes.toMap().toString()}');
    yield ShopOpeningTimes(openingTimes);
  }

  Stream<OpeningTimesState> _mapSaveOpeningTimesToState(
      SaveOpeningTimes event) async* {
    await _openingTimesRepository.saveOpeningTimes(event.openingTimes);
    add(LoadOpeningTimes(event.user));
  }
}

这是存储库:

import 'package:sembast/sembast.dart';

class OpeningTimesRepository {
  // name for the storage
  static const String openingTimeStorage = 'openingTimeStorage';

  // storage reference
  final _openingTimesFolder = intMapStoreFactory.store(openingTimeStorage);

  // database instance
  Future<Database> get _db async => await AppDatabase.instance.database;

  Future saveOpeningTimes(OpeningTimes openingTimes) async {
    print(
        'saveOpeningTimes(): save opening times received ${openingTimes.toMap().toString()}'); // prints correct
    final snapshot = await loadOpeningTimes(userName: openingTimes.userName);
    print('saveOpeningTimes() snapshot is $snapshot');
    if (snapshot == null) {
      print('opening times are to save');
      await _openingTimesFolder.add(await _db, openingTimes.toMap());
    } else {
      print('opening times are to update');
      await updateOpeningTimes(openingTimes);
    }
  }

  Future updateOpeningTimes(OpeningTimes openingTimes) async {
    print(
        'updateOpeningTimes() : update opening time received ${openingTimes.toMap().toString()}'); // correct
    final Finder finder = Finder(filter: Filter.byKey(openingTimes.userName));
    await _openingTimesFolder.update(await _db, openingTimes.toMap(),
        finder: finder);
  }

  Future<OpeningTimes> loadOpeningTimes({String userName}) async {
    print(
        'loadOpeningTimes() called userName is $userName'); // prints correct userName

    final finder = Finder(sortOrders: [SortOrder('userName')]);

    final snapshot =
        await _openingTimesFolder.findFirst(await _db, finder: finder);
    print('loadOpeningTimes() snapshot is: ${snapshot}'); // correct

    return OpeningTimes.fromMap(snapshot.value);
  }


}

这是模型:

import 'package:flutter/material.dart';

class OpeningTimes {
  int id;

  final String userName;

  final String monMorOp;
  final String monMorCl;
  final bool monMorAct;
  final String monAftOp;
  final String monAftCl;
  final bool monAftAct;

  final String tueMorOp;
  final String tueMorCl;
  final bool tueMorAct;
  final String tueAftOp;
  final String tueAftCl;
  final bool tueAftAct;

  final String wedMorOp;
  final String wedMorCl;
  final bool wedMorAct;
  final String wedAftOp;
  final String wedAftCl;
  final bool wedAftAct;

  final String thuMorOp;
  final String thuMorCl;
  final bool thuMorAct;
  final String thuAftOp;
  final String thuAftCl;
  final bool thuAftAct;

  final String friMorOp;
  final String friMorCl;
  final bool friMorAct;
  final String friAftOp;
  final String friAftCl;
  final bool friAftAct;

  final String satMorOp;
  final String satMorCl;
  final bool satMorAct;
  final String satAftOp;
  final String satAftCl;
  final bool satAftAct;

  final String sunMorOp;
  final String sunMorCl;
  final bool sunMorAct;
  final String sunAftOp;
  final String sunAftCl;
  final bool sunAftAct;

  OpeningTimes(
      {@required this.userName,
      @required this.monMorOp,
      @required this.monMorCl,
      @required this.monMorAct,
      @required this.monAftOp,
      @required this.monAftCl,
      @required this.monAftAct,
      @required this.tueMorOp,
      @required this.tueMorCl,
      @required this.tueMorAct,
      @required this.tueAftOp,
      @required this.tueAftCl,
      @required this.tueAftAct,
      @required this.wedMorOp,
      @required this.wedMorCl,
      @required this.wedMorAct,
      @required this.wedAftOp,
      @required this.wedAftCl,
      @required this.wedAftAct,
      @required this.thuMorOp,
      @required this.thuMorCl,
      @required this.thuMorAct,
      @required this.thuAftOp,
      @required this.thuAftCl,
      @required this.thuAftAct,
      @required this.friMorOp,
      @required this.friMorCl,
      @required this.friMorAct,
      @required this.friAftOp,
      @required this.friAftCl,
      @required this.friAftAct,
      @required this.satMorOp,
      @required this.satMorCl,
      @required this.satMorAct,
      @required this.satAftOp,
      @required this.satAftCl,
      @required this.satAftAct,
      @required this.sunMorOp,
      @required this.sunMorCl,
      @required this.sunMorAct,
      @required this.sunAftOp,
      @required this.sunAftCl,
      @required this.sunAftAct});

  @override
  List<Object> get props => [
        userName,
        monMorOp,
        monMorCl,
        monMorAct,
        monAftOp,
        monAftCl,
        monAftAct,
        tueMorOp,
        tueMorCl,
        tueMorAct,
        tueAftOp,
        tueAftCl,
        tueAftAct,
        wedMorOp,
        wedMorCl,
        wedMorAct,
        wedAftOp,
        wedAftCl,
        wedAftAct,
        thuMorOp,
        thuMorCl,
        thuMorAct,
        thuAftOp,
        thuAftCl,
        thuAftAct,
        friMorOp,
        friMorCl,
        friMorAct,
        friAftOp,
        friAftCl,
        friAftAct,
        satMorOp,
        satMorCl,
        satMorAct,
        satAftOp,
        satAftCl,
        satAftAct,
        sunMorOp,
        sunMorCl,
        sunMorAct,
        sunAftOp,
        sunAftCl,
        sunAftAct
      ];
  factory OpeningTimes.fromMap(Map<String, dynamic> map) => new OpeningTimes(
        userName: map['userName'],
        monMorOp: map['monMorOp'],
        monMorCl: map['monMorCl'],
        monMorAct: map['monMorAct'],
        monAftOp: map['monAftOp'],
        monAftCl: map['monAftCl'],
        monAftAct: map['monAftAct'],
        tueMorOp: map['tueMorOp'],
        tueMorCl: map['tueMorCl'],
        tueMorAct: map['tueMorAct'],
        tueAftOp: map['tueAftOp'],
        tueAftCl: map['tueAftCl'],
        tueAftAct: map['tueAftAct'],
        wedMorOp: map['wedMorOp'],
        wedMorCl: map['wedMorCl'],
        wedMorAct: map['wedMorAct'],
        wedAftOp: map['wedAftOp'],
        wedAftCl: map['wedAftCl'],
        wedAftAct: map['wedAftAct'],
        thuMorOp: map['thuMorOp'],
        thuMorCl: map['thuMorCl'],
        thuMorAct: map['thuMorAct'],
        thuAftOp: map['thuAftOp'],
        thuAftCl: map['thuAftCl'],
        thuAftAct: map['thuAftAct'],
        friMorOp: map['friMorOp'],
        friMorCl: map['friMorCl'],
        friMorAct: map['friMorAct'],
        friAftOp: map['friAftOp'],
        friAftCl: map['friAftCl'],
        friAftAct: map['friAftAct'],
        satMorOp: map['satMorOp'],
        satMorCl: map['satMorCl'],
        satMorAct: map['satMorAct'],
        satAftOp: map['satAftOp'],
        satAftCl: map['satAftCl'],
        satAftAct: map['satAftAct'],
        sunMorOp: map['sunMorOp'],
        sunMorCl: map['sunMorCl'],
        sunMorAct: map['sunMorAct'],
        sunAftOp: map['sunAftOp'],
        sunAftCl: map['sunAftCl'],
        sunAftAct: map['sunAftAct'],
      );

  Map<String, dynamic> toMap() => {
        'userName': userName,
        'monMorOp': monMorOp,
        'monMorCl': monMorCl,
        'monMorAct': monMorAct,
        'monAftOp': monAftOp,
        'monAftCl': monAftCl,
        'monAftAct': monAftAct,
        'tueMorOp': tueMorOp,
        'tueMorCl': tueMorCl,
        'tueMorAct': tueMorAct,
        'tueAftOp': tueAftOp,
        'tueAftCl': tueAftCl,
        'tueAftAct': tueAftAct,
        'wedMorOp': wedMorOp,
        'wedMorCl': wedMorCl,
        'wedMorAct': tueMorAct,
        'wedAftOp': wedAftOp,
        'wedAftCl': wedAftCl,
        'wedAftAct': wedAftAct,
        'thuMorOp': thuMorOp,
        'thuMorCl': thuMorCl,
        'thuMorAct': thuMorAct,
        'thuAftOp': thuAftOp,
        'thuAftCl': thuAftCl,
        'thuAftAct': thuAftAct,
        'friMorOp': friMorOp,
        'friMorCl': friMorCl,
        'friMorAct': friMorAct,
        'friAftOp': friAftOp,
        'friAftCl': friAftCl,
        'friAftAct': friAftAct,
        'satMorOp': satMorOp,
        'satMorCl': satMorCl,
        'satMorAct': satMorAct,
        'satAftOp': satAftOp,
        'satAftCl': satAftCl,
        'satAftAct': satAftAct,
        'sunMorOp': sunMorOp,
        'sunMorCl': sunMorCl,
        'sunMorAct': sunMorAct,
        'sunAftOp': sunAftOp,
        'sunAftCl': sunAftCl,
        'sunAftAct': sunAftAct
      };
}

更新存储库方法以使用自动生成的密钥:

static const String openingTimeStorage = 'openingTimeStorage';

  // storage reference
  final _openingTimesFolder = intMapStoreFactory.store(openingTimeStorage);

  // database instance
  Future<Database> get _db async => await AppDatabase.instance.database;

  Future saveOpeningTimes(OpeningTimes openingTimes) async {
    print(
        'saveOpeningTimes(): save opening times received ${openingTimes.toMap().toString()}'); // prints correct
    final snapshot = await loadOpeningTimes();
    print('saveOpeningTimes() snapshot is $snapshot');
    if (snapshot == null) {
      print('opening times are to save');
      await _openingTimesFolder.add(await _db, openingTimes.toMap());
      return;
    } else {
      print('opening times are to update');
      await updateOpeningTimes(openingTimes);
    }
  }

  Future updateOpeningTimes(OpeningTimes openingTimes) async {
    print(
        'updateOpeningTimes() : update opening time received ${openingTimes.toMap().toString()}'); // correct
    final Finder finder = Finder(
        filter: Filter.byKey(
            _openingTimesFolder.record(openingTimes.id).get(await _db)));

    await _openingTimesFolder.update(await _db, openingTimes.toMap(),
        finder: finder);
  }

  Future<OpeningTimes> loadOpeningTimes() async {
    final finder = Finder(sortOrders: [SortOrder('userName')]);

    final snapshot =
        await _openingTimesFolder.findFirst(await _db, finder: finder);
    if (snapshot != null) {
      print('loadOpeningTimes() snapshot is: $snapshot'); // correct
      OpeningTimes openingTimes = OpeningTimes.fromMap(snapshot.value);
      openingTimes.id = snapshot.key;
      return openingTimes;
    } else {
      return null;
    }
  }

更新时我收到flutter: Erros is Invalid argument(s): Record key cannot be null

【问题讨论】:

    标签: database flutter


    【解决方案1】:

    updateOpeningTimes 中,您的过滤器看起来不正确。

    final Finder finder = Finder(filter: Filter.byKey(openingTimes.userName));

    在这里,您正在寻找以 userName 作为键的对象,但这里的键是 int(自动生成),因此无法匹配。你应该有类似的东西:

    final Finder finder = Finder(filter: Filter.equals('userName', userName));
    

    loadOpeningTimes 中,您只需获取按用户名排序的第一条记录。如果您查找显式用户名,则应使用与更新相同的查找器。

    顺便说一句,按键加载记录要快得多:store.record(key).get(db)

    【讨论】:

    • 您好,感谢您的回答。确实使用.equals 是搜索它的正确方法。现在这是我不明白的事情:如何使用自动生成密钥保存我的OpeningTimes 模型的id 参数?查看我更新的存储库代码。
    • 在各种代码示例中,我看到id 不包含在fromMaptoMap 模型的方法中......所以在loadOpeningTimes 中按id 排序导致空快照
    • github.com/tekartik/sembast.dart/issues/68 中找到了解决方案。非常感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-29
    • 2020-11-08
    • 2020-01-28
    • 2022-12-01
    • 2019-11-20
    • 2020-10-27
    • 2015-07-23
    相关资源
    最近更新 更多