我有一个sqflite数据库,该数据库的表除了所有列外还设置了所有表,而唯一键设置为BLOB数据类型。目的是将其存储为二进制数据。我对飞镖和扑打非常陌生,所以我将事情变得尽可能简单。这些列中有int
,double
,String
,List<String>
,Map<String:String>
和DateTime
数据。我使用的是.insert()函数,而不是.rawInsert()函数。
在我的TEST application中,将字符串或int作为BLOB数据类型放入表中都没有问题。我使用完全相同的代码插入,只是具有不同列的不同对象。在要用于生产的应用程序中,它具有更大,更长的字符串(如base 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;
}
这是数据库中使用的Product model。
我将生产代码模型中表示的所有数据类型添加到我的测试应用程序模型中,并且能够复制该错误。请参阅顶部的测试应用程序链接。我一步一步地删除了新的数据类型,直到错误消失,然后才发现是导致此错误的List数据类型。我仔细检查了地图,认为不会造成任何麻烦。
那么,精致的问题:如何使用sqflite存储字符串列表?我不想只是将其存储为原始JSON字符串...那会很烂...
答案 0 :(得分:1)
您必须展平模型。请参阅supported types帮助部分。
基本上仅支持int
,double
,String
和Uint8List
(blob)类型。
不幸的是,您必须转换内部List<String>
和Map<String:String>
,json是一种解决方案。
答案 1 :(得分:0)
经过研究,我认为我对BLOB的理解是不正确的-我认为这意味着它将对象存储为二进制数据,并且我还假设在SQLite中,如果表是BLOB,则将强制所有数据进行转换。插入时转换为二进制,如果不是则至少抛出。事实证明,SQLite并不严格,而BLOB仅表示“您想要的任何数据类型”。我还发现SQLite会尝试自动转换对象的数据类型。因此,需要将复杂的数据类型(例如List)转换为SQLite才能转换为自己的类型,然后再进行存储。 SQLite中没有数组或对象类型,因此列表将不起作用。
我解决了这个问题,方法是在插入数据库之前将复杂的数据类型转换为JSON字符串,然后从数据库中获取/实例化对象时将其转换回List。这样,我不必每次需要使用/更改该属性或其中的任何内容时都将对象属性从JSON转换出来。并不完全优雅,但是它可以正常工作,而且我了解。