У меня есть база данных sqflite с таблицей, настроенной со всеми столбцами, кроме уникального ключа с типом данных BLOB. Целью является хранение в виде двоичных данных. Я очень новичок в дартс и флаттер, поэтому я стараюсь делать все как можно проще. У меня есть int, double, String, List<String>, Map<String:String> и DateTime данные, поступающие в эти столбцы. Я использую функцию .insert (), а не функцию .rawInsert ().

В моем приложении TEST нет никаких проблем, если строка или int входит в таблицу как тип данных BLOB , Я использую один и тот же код для вставки, просто разные объекты с разными столбцами. В приложении, которое я создаю для производства, которое имеет гораздо большие, более длинные строки (например, изображения в базе 64) и намного большие целые числа, я получаю ошибку.

Странно то, что у меня есть код проверки типа, который преобразует все целочисленные данные, которые я получаю с моего сервера, в реальные целочисленные данные ... нет никакого способа, которым целые числа могли бы быть чем угодно, кроме целых чисел. Каждое значение проходит через функцию, которая может только бросить или вернуть целое число.

Как тогда база данных получит строку, которую она пытается преобразовать в целое число?

Я не прошу решения, так как я должен предпринять дополнительные шаги по устранению неполадок, которые я мог бы пропустить из-за усталости мозга или неопытности. Я столкнулся с блокпостом.

Вот трассировка стека для возбуждаемого исключения:

I/flutter ( 6502): #0      wrapDatabaseException (package:sqflite/src/exception_impl.dart:11:7)
I/flutter ( 6502): <asynchronous suspension>
I/flutter ( 6502): #1      SqfliteDatabaseFactoryImpl.wrapDatabaseException (package:sqflite/src/factory_impl.dart:29:7)
I/flutter ( 6502): #2      _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin.safeInvokeMethod (package:sqflite/src/database_mixin.dart:183:15)
I/flutter ( 6502): #3      _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin.txnRawInsert.<anonymous closure> (package:sqflite/src/database_mixin.dart:340:14)
I/flutter ( 6502): #4      _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin.txnSynchronized.<anonymous closure> (package:sqflite/src/database_mixin.dart:290:22)
I/flutter ( 6502): #5      BasicLock.synchronized (package:synchronized/src/basic_lock.dart:31:26)
I/flutter ( 6502): <asynchronous suspension>
I/flutter ( 6502): #6      _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin.txnSynchronized (package:sqflite/src/database_mixin.dart:286:43)
I/flutter ( 6502): <asynchronous suspension>
I/flutter ( 6502): #7      _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin.txnWriteSynchronized (package:sqflite/src/database_mixin.dart:307:7)
I/flutter ( 6502): #8      _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin.txnRawInsert (package:sqflite/src/database_mixin.dart:339:12)
I/flutter ( 6502): #9      _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin&SqfliteDatabaseExecutorMixin.rawInsert (package:sqflite/src/database_mixin.dart:44:15)
I/flutter ( 6502): #10     _SqfliteDatabaseBase&Object&SqfliteDatabaseMixin&SqfliteDatabaseExecutorMixin.insert (package:sqflite/src/database_mixin.dart:54:12)
I/flutter ( 6502): #11     AppPersistenceManager.insertIntoDatabase (package:gogreen_utility_belt/app/AppPersistenceManager.dart:120:37)
I/flutter ( 6502): <asynchronous suspension>
I/flutter ( 6502): #12     AppNetwork._triggerProductFetchAndCacheWith.<anonymous closure> (package:gogreen_utility_belt/app/AppNetwork.dart:167:38)
I/flutter ( 6502): #13     _rootRunUnary (dart:async/zone.dart:1132:38)
I/flutter ( 6502): #14     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
I/flutter ( 6502): #15     _FutureListener.handleValue (dart:async/future_impl.dart:126:18)
I/flutter ( 6502): #16     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:639:45)
I/flutter ( 6502): #17     Future._propagateToListeners (dart:async/future_impl.dart:668:32)
I/flutter ( 6502): #18     Future._chainCoreFuture (dart:async/future_impl.dart:454:7)
I/flutter ( 6502): #19     Future._complete (dart:async/future_impl.dart:466:9)
I/flutter ( 6502): #20     _SyncCompleter.complete (dart:async/future_impl.dart:51:12)
I/flutter ( 6502): #21     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:28:18)
I/flutter ( 6502): #22     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:294:13)
I/flutter ( 6502): #23     compute (package:flutter/src/foundation/_isolates_io.dart)
I/flutter ( 6502): <asynchronous suspension>
I/flutter ( 6502): #24     AppNetwork._triggerProductFetchAndCacheWith (package:gogreen_utility_belt/app/AppNetwork.dart:153:55)
I/flutter ( 6502): <asynchronous suspension>
I/flutter ( 6502): #25     AppNetwork.fetchAndCacheAllProducts (package:gogreen_utility_belt/app/AppNetwork.dart:195:9)
I/flutter ( 6502): #26     _asyncThenWrapperHelper.<anonymous closure> (dart:async-patch/async_patch.dart:77:64)
I/flutter ( 6502): #27     _rootRunUnary (dart:async/zone.dart:1132:38)
I/flutter ( 6502): #28     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
I/flutter ( 6502): #29     _FutureListener.handleValue (dart:async/future_impl.dart:126:18)
I/flutter ( 6502): #30     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:639:45)
I/flutter ( 6502): #31     Future._propagateToListeners (dart:async/future_impl.dart:668:32)
I/flutter ( 6502): #32     Future._complete (dart:async/future_impl.dart:473:7)
I/flutter ( 6502): #33     _cancelAndValue (dart:async/stream_pipe.dart:63:12)
I/flutter ( 6502): #34     Stream.first.<anonymous closure> (dart:async/stream.dart:1190:11)
I/flutter ( 6502): #35     _rootRunUnary (dart:async/zone.dart:1132:38)
I/flutter ( 6502): #36     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
I/flutter ( 6502): #37     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
I/flutter ( 6502): #38     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
I/flutter ( 6502): #39     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:263:7)
I/flutter ( 6502): #40     _SyncBroadcastStreamController._sendData (dart:async/broadcast_stream_controller.dart:375:20)
I/flutter ( 6502): #41     _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:250:5)
I/flutter ( 6502): #42     _AsBroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:474:11)
I/flutter ( 6502): #43     _rootRunUnary (dart:async/zone.dart:1136:13)
I/flutter ( 6502): #44     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
I/flutter ( 6502): #45     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
I/flutter ( 6502): #46     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
I/flutter ( 6502): #47     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:263:7)
I/flutter ( 6502): #48     _SyncStreamController._sendData (dart:async/stream_controller.dart:764:19)
I/flutter ( 6502): #49     _StreamController._add (dart:async/stream_controller.dart:640:7)
I/flutter ( 6502): #50     _StreamController.add (dart:async/stream_controller.dart:586:5)
I/flutter ( 6502): #51     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)

Вот как выглядят некоторые из задействованных функций:

initDatabase() async {
  print(new Trace.from(StackTrace.current).terse.frames[0]);
  Directory documentsDirectory;
  print(
      'documentsDirectory set to null, about to assign with an await method');
  documentsDirectory = await getApplicationDocumentsDirectory();
  print('passed doc dir');
  String path = join(documentsDirectory.path, "TestDB.db");
  print('passed join');
  print(Trace.from(StackTrace.current).terse);
  return await openDatabase(path, version: 1, onOpen: (db) {
    print('in onOpen');
  }, onCreate: (Database database, int version) async {
    print('in onCreate async');
    for (DatabaseTable databaseTable in DatabaseTable.values) {
      print(
          'reached inside of "for (DatabaseTable databaseTable in DatabaseTable.values)"');
      String executionString =
          "CREATE TABLE IF NOT EXISTS ${RawValue.databaseTable(databaseTable)}("
          "$uniqueRowKey INTEGER PRIMARY KEY,";

      JSONSerializable objectExample;
      switch (databaseTable) {
        case DatabaseTable.Product:
          objectExample = Product.example;
      }
      print('getting parameters');
      List<String> parameters = [];
      for (String key
          in getDatabaseKeyedMapFrom(serialObject: objectExample).keys) {
        parameters.add(key);
      }
      print('getting unique keys');
      List<String> uniqueKeys = [];
      var rawUniqueProperties = objectExample.uniqueProperties();
      for (String property in rawUniqueProperties) {
        uniqueKeys.add(getDatabaseKeyFrom(jsonKey: property));
      }
      print('iterating through parameters');
      for (String parameter in parameters) {
        if (parameter != parameters.first) {
          executionString += ',';
        }
        if (uniqueKeys.contains(parameter)) {
          uniqueKeys.remove(parameter);
          executionString += "$parameter BLOB UNIQUE";
          continue;
        }
        executionString += "$parameter BLOB";
      }
      executionString += ')';
      print('executing string on database');
      await database.execute(executionString);
    }
  });
}

/// Attempts to insert the object into its corresponding table.
Future<DatabaseLocation> insertIntoDatabase(JSONSerializable object) async {
  print(new Trace.from(StackTrace.current).terse.frames[0]);
  final localDatabase = await database;
  String tableName = RawValue.databaseTable(getTableFor(object));
  try {
    Map<String, dynamic> dbKeyedMap =
        getDatabaseKeyedMapFrom(serialObject: object);
    int row = await localDatabase.insert(tableName, dbKeyedMap,
        conflictAlgorithm: ConflictAlgorithm.replace);
    return DatabaseLocation(table: tableName, row: row);
  } catch (error, trace) {
    printWrapped(error.toString());
    printWrapped(trace.toString());
    rethrow;
  }
}

///converts the String keys of any map representing an object into snake_case
Map<String, dynamic> getDatabaseKeyedMapFrom(
    {JSONSerializable serialObject}) {
  print(new Trace.from(StackTrace.current).terse.frames[0]);
  Map<String, dynamic> jsonKeyedMap = serialObject.toJson();
  List<String> jsonPropertyNames = serialObject.propertyNames();
  Map<String, dynamic> databaseKeyedMap = {};
  for (String jsonPropertyName in jsonPropertyNames) {
    ReCase reCase = ReCase(jsonPropertyName);
    String databasePropertyName = reCase.snakeCase;
    databaseKeyedMap[databasePropertyName] = jsonKeyedMap[jsonPropertyName];
  }
  return databaseKeyedMap;
}

///converts the String keys of any map representing an object into camelCase
Map<String, dynamic> getJSONKeyedMapFrom(
    {Map<String, dynamic> databaseKeyedMap}) {
  ////print(new Trace.from(StackTrace.current).terse.frames[0]);
  List<String> databasePropertyNames = databaseKeyedMap.keys;
  Map<String, dynamic> jsonKeyedMap = {};
  for (String databasePropertyName in databasePropertyNames) {
    ReCase reCase = ReCase(databasePropertyName);
    String jsonPropertyName = reCase.camelCase;
    jsonKeyedMap[jsonPropertyName] = databaseKeyedMap[databasePropertyName];
  }
  return jsonKeyedMap;
}

Вот модель продукта, используемая в БД.

Обновить:

Я добавил все типы данных, представленные в модели производственного кода, в свою модель тестового приложения и смог продублировать ошибку. Смотрите ссылку на тестовое приложение вверху. Я удалял новые типы данных по одному, пока ошибка не исчезла, и смог выяснить, что именно тип данных List генерирует эту ошибку. Я дважды проверил карту, чтобы не было никаких проблем.

Итак, уточненный вопрос: как мне сохранить список строк, используя sqflite? Я не хочу просто хранить его как исходную строку JSON ... это было бы отстой ...

1
A. L. Strine 15 Авг 2019 в 20:54

2 ответа

Лучший ответ

Вы должны сгладить свою модель. См. раздел справки по поддерживаемым типам.

В основном, int, double, String и Uint8List (blob) являются единственными поддерживаемыми типами. К сожалению, вы должны преобразовать свои внутренние List<String> и Map<String:String>, json является одним из решений.

1
alextk 15 Авг 2019 в 21:47

После некоторого исследования, я думаю, что мое понимание BLOB было неверным - я предполагал, что это означает, что он будет хранить объекты как двоичные данные, и я также предположил, что в SQLite, если таблица была BLOB, это заставило бы все данные преобразовываться в двоичные данные, когда вставлен, или, по крайней мере, бросить, если это не так. Оказывается, что SQLite не является строгим, а BLOB просто означает «любой тип данных, который вы хотите». Я также обнаружил, что SQLite попытается автоматически преобразовать тип данных объектов. Поэтому сложные типы данных, такие как List, необходимо преобразовать во что-то, что SQLite может преобразовывать в свои собственные типы перед сохранением. В SQLite нет типа Array или Object, поэтому список просто не будет работать.

Я решил эту проблему, преобразовав сложный тип данных в строку JSON перед вставкой в БД, и преобразовав его обратно в список при извлечении / создании объекта из базы данных. Таким образом, мне не нужно иметь дело с преобразованием свойства объекта из JSON каждый раз, когда мне нужно использовать / изменить это свойство или что-то в нем. Не совсем элегантно, но это работает, и я это понимаю.

1
A. L. Strine 15 Авг 2019 в 22:21