(ОБНОВЛЕНИЕ: Как сохранить тип данных List ‹String› в sqflite) Ошибка sqflite: DatabaseException (java.lang.String не может быть преобразован в java.lang.Integer)

У меня есть база данных 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 ... это было бы отстой ...


person A. L. Strine    schedule 15.08.2019    source источник


Ответы (2)


Вы должны сплющить вашу модель. См. Раздел справки поддерживаемых типов.

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

person alextk    schedule 15.08.2019
comment
Спасибо! На самом деле я не сталкивался с какими-либо проблемами с Map ‹String: String›, но я все равно сгладю его на всякий случай. - person A. L. Strine; 16.08.2019

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

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

person A. L. Strine    schedule 15.08.2019