(UPDATE: How to store List<String> data type in sqflite) sqflite error: DatabaseException(java.lang.String cannot be cast to java.lang.Integer)

我与影子孤独终老i 提交于 2020-04-17 22:43:27

问题


I have a sqflite database with a table set up with all columns aside form the unique key set to BLOB data type. The intent is to store as binary data. I'm very new to dart and flutter, so I'm keeping things as simple as possible. I have int, double, String, List<String>, Map<String:String>, and DateTime data going into these columns. I am using the .insert() function, not the .rawInsert() function.

In my TEST application there is no problem whatsoever with either string or int going into the table as BLOB data type. I'm using exactly the same code to insert, just different objects with different columns. In the application I'm building for production, which has much larger, longer strings (like images in base 64), and much larger integers, I'm getting the error.

The strange thing is, I have type checking code that is converting all of the integer data I'm getting from my server in to actual integer data...there's no possible way that the integers could be anything but integers. Every value goes through a function that can only throw, or return an integer.

How then would the database be getting a string that it is trying to convert into an integer?

I'm not asking for a solution, so much as additional troubleshooting steps I should take that I might be missing due to brain fatigue or inexperience. I've hit a roadblock of sorts.

Here is the stack trace for the exception being raised:

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)

Here is what some of the functions involved look like:

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;
}

Here is the Product model used in the DB.

Update:

I added all of the data types represented in the production code model to my test app model, and was able to duplicate the error. See the test application link at the top. I removed the new data types one by one until the error went away, and was able to find out that it's the List data type that generates this error. I double checked the Map, that one doesn't cause any trouble.

So, refined question: how do I store a list of strings using sqflite? I don't want to just store it as the original JSON string...that would suck...


回答1:


You have to flatten your model. See the supported types help section.

Basically int, double, String and Uint8List(blob) are the only types supported. Unfortunately you have to convert your inner List<String> and Map<String:String>, json being one solution.




回答2:


After some research, I think my understanding of BLOB was incorrect - I assumed it meant it would store the objects as binary data, and I also assumed that in SQLite, if a table was BLOB it would force all data to be converted to binary when inserted, or at least throw if it isn't. Turns out that SQLite is not strict, and BLOB just means 'any data type you want'. I also discovered that SQLite will try to automatically convert the data type of objects. So complex data types such as List need to be converted into something that SQLite can cast into its own types before being stored. There is no Array or Object type in SQLite, so a List just won't work.

I solved this problem by converting the complex data type to a JSON string before inserting into the DB, and converting it back into a List when fetching/instantiating the object from the database. That way I don't have to deal with converting the object property out of JSON every time I need to use/change that property or something in it. Not exactly elegant, but it works and I understand it.



来源:https://stackoverflow.com/questions/57514027/update-how-to-store-liststring-data-type-in-sqflite-sqflite-error-database

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!