casatwy / ctpersistance Goto Github PK
View Code? Open in Web Editor NEWiOS Database Persistence Layer with SQLite, your next Persistence Layer!
License: Other
iOS Database Persistence Layer with SQLite, your next Persistence Layer!
License: Other
当存储的字段为字符串类型,且字符串中有 ; 符号的时候, 会被safeSQLEncode 过滤掉,导致数据存储错误
Hi casa,
我想在 Swift 项目中 尝试使用 CTPersistance,通过 cocoapods 集成后,编译出错:
libsqlte3.tbd 添加了,我的 Podfile 是这样的:
platform :ios, '8.0'
use_frameworks!
target 'CTPersistanceDemo' do
pod 'CTPersistance'
end
而集成其他的 objc 写的第三方 pod 都没问题,比如 JGProgressHUD。
麻烦你看看,先谢谢了。
备注:
数据库链接池存在一些问题,我提一下:
我的需求场景是:如果表中没有数据,就是插入最新数据;如果表中有数据,就用最新数据替换表中数据。而目前CTPersistance中已经提供的分类没有这样给我提供sql语句的方法。
在插入新数据之后会强制把 lastInsertRowId
设置为主键的值,但是这个值类型和数据是错误的, 请问该如何处理这种情况?
如题
static NSString * kCTPersisatanceConfigurationFileName
->
static NSString * const kCTPersisatanceConfigurationFileName
version: swift4 edition
HandyJSON version 1.8.0
问题:在BuiltInBasicType.swift文件中的第30行
protocol IntegerPropertyProtocol: Integer, _BuiltInBasicType
提示Integer has been renamed to BinaryInteger
CTPersistanceAsyncExecutor 的 operationQueue 是不是应该在单例初始化的时候就初始化好?否则多线程同时调用这个单例会出问题。
在进行数据库记录更新时,如果存在 BLOB 字段,那么可能出现问题。默认情形下,数据更新操作并不会调用 bindBlobWithStatement 函数,也就是说 sqlite3_bind_blob 不会被执行,这样会导致一个 BLOB 类型字段不会被更新,甚至被更新为 NULL。除非再额外调用一次 BLOB 类型字段 对应的更新操作。
// 该操作不会更新 BLOB 类型字段
[table updateRecord:record error:&error];
// 需要以下操作才能实现真正的更新
[table updateRecord:record error:&error];
[table updateValue:record.bleData forKey:@"bleData" primaryKeyValue:record.identifier error:&error];
不知道作者能否在后面对 updateRecord 进行补充?
我在文档里看到
方法:
有什么用处,谢谢~
今天更新插件的时候升级到了1.3.2,发现数据库操作全都失效了。看了下楼主的提交更新才明白了又是之前那个''的问题。
针对这种 NSString *sqlString = @"SELECT * FROM ':tableName' WHERE conversationId = ':conversationId' ORDER BY messageTime DESC limit ':index',':count'";
语法,前面带:冒号的参数都增加了'',查询OK了
但是
NSArray *recordList = [self.contactInfoTable findAllWithKeyName:@"shortName" value:shortName error:&error];
这种查询又失效了,点进去看了一下,分类里面参数楼主没有加''
这里似乎又回到了1.3.0的缺少''语法问题
之前的问题 #37 就是这个问题
数据库我不熟,语法也不太懂,只是测试出来是这个问题,楼主看下
Hello and thanks for the great source code.
I have a question. How can i handle an NSArray in the testrecord for example in your demo?
as i see it i need to add the column in the testtable.m file as well.
how will i add the array in the table?
thanks
请问,如何做多表联合查询呢?
错误信息的一部分“WHERE primaryKey = 987D5D73-4E98-49D5-A095-19610A3E8EEB
Error Message is: unrecognized token: "987D5D73"”
应该是“- (NSString *)stringWithSQLParams:(NSDictionary *)params”这个方法在拼接参数的使用没有把主键值用“''”单引号包起来的原因。
改成""... WHERE primaryKey = '987D5D73-4E98-49D5-A095-19610A3E8EEB' ”就好了
代码中有太多调用系统的NSLog来打印日志了,能否用个宏控制下,Debug模式下用NSLog打印,Release模式下不打印,稍微优化下
/* 3002 */
NSDictionary *keyvalueList = @{@"migration1_0":@"this is migration"};
NSString *whereCondition = @":primaryKey > 0";
NSString *primaryKey = [table primaryKeyName];
NSDictionary *whereConditionParams = NSDictionaryOfVariableBindings(primaryKey);
[table updateKeyValueList:keyvalueList whereCondition:whereCondition whereConditionParams:whereConditionParams error:&error];
/* test 3003 */
以这段代码为例:
if ([valueType isEqualToString:@"INTEGER"]) {
invocation = [NSInvocation invocationWithMethodSignature:[NSMutableArray instanceMethodSignatureForSelector:@selector(bindIntegerWithStatement:value:key:)]];
invocation.target = self;
invocation.selector = @selector(bindIntegerWithStatement:value:key:);
[invocation setArgument:&bindValue atIndex:3];
[invocation setArgument:&bindKey atIndex:4];
[invocation retainArguments];
[self addObject:invocation];
return;
}
invocation = [NSInvocation invocationWithMethodSignature:[NSMutableArray instanceMethodSignatureForSelector:@selector(bindIntegerWithStatement:value:key:)]];
invocation.target = self;
这两句造成了循环引用,建议转为单例调用
为了提高用户体验,我们在将用户操作与api分开了,准备开发一个带有自动检测是否有要update,load,insert等通过api进行下载,上传,更新本地数据库功能,请问推荐使用这个吗?
我使用一个 double 字段保存毫秒数时间戳,但是使用 updateRecord 进行操作时候发生异常错误,调用栈为:
最终发现错误出现在:
NSString *valueType = [[[columnDescription componentsSeparatedByString:@" "] firstObject] uppercaseString];
if (valueType == nil) {
if ([bindValue isKindOfClass:[NSNumber class]]) {
valueType = @"INTEGER";
} if ([bindValue isKindOfClass:[NSString class]]) {
valueType = @"TEXT";
} if ([bindValue isKindOfClass:[NSNull class]]) {
valueType = @"INTEGER";
} if ([bindValue isKindOfClass:[NSData class]]) {
valueType = @"BLOB";
}
}
您将 double 类型重置为了整型从而导致,REAL 类型更新出现异常,根本原因在于所有调用 bindToValueList 都会存在这个问题。预估其他操作使用处也会存在问题。
当主键是字符串的时候,在获取操作中有findAllWithKeyName可以进行查找。但是当需要通过主键删除一条记录的时候就没有相应的方法了,可不可以在删除deleteRecord的系列方法中也添加类似deleteRecordWithKeyName呢,会不会有其他隐患?
类型绑定 addBindKey 函数在更新 BOOL 类型时还是会出现问题。理论上 SQLite 好像是没有 BOOL 类型的,所以绑定代码可以做出部分修改:
if ([bindValue isKindOfClass:[NSNumber class]]) {
NSNumber *value = (NSNumber *)bindValue;
valueType = @"INTEGER";
if (strcmp(value.objCType, @encode(float)) == 0
|| strcmp(value.objCType, @encode(double)) == 0
|| strcmp(value.objCType, @encode(CGFloat)) == 0) {
valueType = @"REAL";
}
}
并去掉:
if ([valueType isEqualToString:@"BOOLEAN"]) {
invocation = [NSInvocation invocationWithMethodSignature:[NSMutableArray instanceMethodSignatureForSelector:@selector(bindBooleanWithStatement:value:key:)]];
invocation.target = self;
invocation.selector = @selector(bindBooleanWithStatement:value:key:);
[invocation setArgument:&bindValue atIndex:3];
[invocation setArgument:&bindKey atIndex:4];
[invocation retainArguments];
[self addObject:invocation];
return;
}
不然会将 BOOL 类型更新为 NULL,然后你取出后变为 NSNULL,然后就出现运行时错误。
晚上先到这里吧,明天起床再说早点休息
Query Error:
Origin Query is : INSERT INTO BBCMessageDetailTable
(id
,isReaded
,sendTime
,fromId
,toId
,owner
,msgType
,orderId
,orderType
,peer
,status
,content
) VALUES (NULL,'0','1467975653','16122','22','16122','0','157','2','22','2','I'm ');
sqlite> INSERT INTO BBCMessageDetailTable
(id
,isReaded
,sendTime
,fromId
,toId
,owner
,msgType
,orderId
,orderType
,peer
,status
,content
) VALUES (NULL,'0','1467975653','16122','22','16122','0','157','2','22','2','I'm ');
...> );
...> ');
Error: near "m": syntax error
sqlite> INSERT INTO BBCMessageDetailTable
(id
,isReaded
,sendTime
,fromId
,toId
,owner
,msgType
,orderId
,orderType
,peer
,status
,content
) VALUES (NULL,'0','1467975653','16122','22','16122','0','157','2','22','2','I''m ');
sqlite> select * from BBCMessageDetailTable;
0|0|1467972148|22|Q really |16122|0|156|2|22|1|16122
1|0|1467972118|22|The best |16122|0|156|2|22|2|16122
2|0|1467975594|22|Even |16122|0|157|2|22|1|16122
3|0|1467975622|22|Do not |16122|0|157|2|22|1|16122
4|0|1467975653|22|Im |16122|0|157|2|22|2|16122
5|0|1467975653|22|I'm |16122|0|157|2|22|2|16122
- (BOOL)setPersistanceValue:(id)value forKey:(NSString *)key
{
BOOL result = YES;
NSString *setter = [NSString stringWithFormat:@"set%@%@:", [[key substringToIndex:1] capitalizedString], [key substringFromIndex:1]];
if ([self respondsToSelector:NSSelectorFromString(setter)]) {
if ([value isKindOfClass:[NSString class]]) {
[self setValue:[value safeSQLDecode] forKey:key];
} else if ([value isKindOfClass:[NSNull class]]) {
[self setValue:nil forKey:key];
} else {
[self setValue:value forKey:key];
}
} else {
result = NO;
}
return result;
}
在判断 [value isKindOfClass:[NSNull class]]分支里,value被设置为nil,导致crash
could not set nil as the value for the key
情景:
新建migratorStep,实现
- (void)goUpWithQueryCommand:(CTPersistanceQueryCommand *)queryCommand error:(NSError *__autoreleasing *)error
{
[[queryCommand addColumn:@"uploadFinishDate" columnInfo:@"INTEGER" tableName:@"LocalMovieTable"] executeWithError:error];
}
数据库中增加一个INTEGER类型的字段。数据库升级之后,由于新添字段,所以会走[value isKindOfClass:[NSNull class]]的分支,然而,这里uploadFinishDate并不是id类型,导致出错。
场景:我有多个业务模块,分别做成不同的私有Pod。其中每个业务模块都使用CTPersistance创建自己模块数据库,每个数据库也都有对应的Migrator。但是有个问题,CTPersistance提供的配置文件CTPersistanceConfiguration.plist只有一份,如果在私有库中的CTPersistanceConfiguration.plist文件配置,当合并到主工程时,如果主工程或者别的私有库也有CTPersistanceConfiguration.plist文件,那么就会出现文件重复。这样的场景下,即如果有分库的需求时,如何进行数据迁移的配置呢?
谢谢~~
在CTPersistanceTable+Update.m
的方法
- (void)updateRecordList:(NSArray <NSObject <CTPersistanceRecordProtocol> *> *)recordList error:(NSError **)error
,
若直接传入error值是nil或NULL,运行到这个方法会崩溃Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
,错误定位在29行 if (*error != nil) {
,若传入的error 为 &error 则正常。
当Record中的某个字段为UUID格式 (如4885D273-C3C6-40AE-9504-A147C4B3ACFB),这条数据,可以插入到数据库,但是通过这个字段的值去查询的时候,查不到这个数据。
然后,我又测试了你的test案例,这种情况同样的是出现的,在insert的例子中
//Insert
NSError error = nil;
/ test 1001 */
TestTable *table = [[TestTable alloc] init];
TestRecord *record = [[TestRecord alloc] init];
record.age = @(1);
record.name = @"4885D273-C3C6-40AE-9504-A147C4B3ACFB";
record.tomas = @"1";
[table insertRecord:record error:&error];
if ([record.primaryKey integerValue] > 0) {
NSLog(@"1001 success");
} else {
NSException *exception = [[NSException alloc] init];
@throw exception;
}
//
//Fetch
error = nil;
NSArray *recordList = [table findAllWithKeyName:@"name" value:@"4885D273-C3C6-40AE-9504-A147C4B3ACFB" error:&error];
if (recordList.count == 0) {
NSException *exception = [[NSException alloc] init];
@throw exception;
} else {
NSLog(@"2021 success");
}
NSLog(@"End Fetch Test");
//
record中的name字段,在我的测试过程中,发现name为UUID格式时,或者也可以说不全为数字时,都是查询不到的,我不知道是不是我使用方式错误,遗漏了某些地方,还是这里是有bug的
update:
原来是这个问题,TEXT类型的数据,需要加‘’,否则如果出现像UUID这种格式的字符串,就无法判别,只拿到前面的那些数字...
不知道这算不算个问题,或者,你可以在你的NSString里面多加一个方法,预防一下这个问题的出现
从CTPersistanceRecordProtocol文件的的声明config your record with dictionary和CTPersistanceRecord的实现来看这个方法都偏向于是一个动作,是不是名称改成- (void)setPersistanceValuesForKeysWithDictionary:(NSDictionary *)keyedValues会更合适?还是这里这么命名是有其他深层的原因?
CTPersistance.m中
- (void)configTable:(CTPersistanceQueryCommand *)queryCommand
[queryCommand createIndex:obj[kCTPersistanceTableIndexName]
tableName:self.child.tableName
indexedColumnList:obj[kCTPersistanceTableIndexedColumnList]
isUnique:[obj[kCTPersistanceTableIndexIsUniq] boolValue]];
这句没有执行executeWithError:
导致表的indexes没有创建
pod "CTPersistance"
我一直搞不懂一个问题,如果服务器将错误的数据类型返回给客户端(能使app崩溃),此时客户端将此数据保存了下来,那么对于app来说岂不是很危险。这种情况该怎么避免呢
对于数据库 NULL 值当前设计是在 fetchWithError 函数中将其设置为 [NSNull null] 单例对象。在 OC 环境下这种设计绝大多数情形下不会存在太大的问题,但是对于 Swift 混编环境来说该设计明显对 Optional 值不友好,使用不当存在潜在 Crash 问题。另一个是对于值对象来说:即使是 OC 环境,[NSNull null] 单例对象在动态转化为 long long 等值时存在 Runtime Crash (当然这个在插入记录的时候就存在使用不当,值类型不应使用空值,但是 CT Runtime 提供了这部分操作的能力 )。
如果我们将改部分处理代码改为:
case SQLITE_NULL:
[result setObject:nil forKey:columnName];
break;
可能对使用者来说更好一点。
另一个,空值改进的方法我觉得后期可以考虑一下:对 CTPersistanceTableProtocol 进行一个拓展,增加一个 columnDefaultValue 方法用于设置默认值。这样使用者可以通过该协议设置默认值从而避免空值对象问题,而不是在插值之前人为保证。
should differentiate key and value in whereCondition, and only string type of value should have semi-colon.
在CTPersistanceQueryCommand.m中
case SQLITE_INTEGER:
{
int value = sqlite3_column_int(statement, i);
[result setObject:[NSNumber numberWithInt:value] forKey:columnName];
break;
}
从数据库中读出的整形,被转化为NSNumber,不是通过@(num),而是通过[NSNumber numberWithInt:value]的方式。
目前遇到的问题是:
我把一个文件的大小存入数据库,size超过4GB(>32位,Int)。从数据库取出来的值出错了。
Hi,大神!我又来麻烦您了。现在我有一份数据库文件,它是在Bundle中的,是我用来记录了一些常量,其中表都是类似这样子的
其中StringForAppDisplay是给App显示用的,CodeForServer是和后台服务器交互用的。主要是为了解决同样的数据,App显示数据和后台交互数据的不同的问题。
之前是自己用FMDB写SQL来查,现在想复用CTPersistance中的查询功能。但是看CTPersistanceDataBase的源码发现,CTPersistance能操作的数据库都只是在App的Library文件夹中,并不能访问到Bundle中的数据库文件。
我想请教一下:
1、如果我想复用CTPersistance的查询功能访问Bundle中的数据库文件,有什么比较优雅的做法没?
2、我用bundle中的数据库文件记录这种常量的设计是否合理(之前我是用Plist做,但是考虑到Plist容易被别人解包直接拿到数据,而且每次加载需要把整个Plist加载进内存。于是我改成了数据库的方式。数据库有设置加密密码,提高了一些数据安全性。并且我有配合NSCache来解决多次重复查询需要大量时间的问题)?如果不合理能否帮忙给点建议?
谢谢。
while (shouldWait) {
}
这个循环语句在release下编译,编译器会认为shouldWait不会被修改而不会每次去取值,导致死循环。在shouldWait变量前声明volatile可解决。另外,为啥不直接用- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait?
castaway,你好,有想过改成 swift 版本吗,我有意愿
创建表被设计成只能指定字段及类型后,请问应该如何去添加外键约束呢?
ReadMe.md,word"confirms" should change to "conforms"
我需要实现一个功能:向一个数据库表中merge数据。如果表中已有该数据就update数据,如果表中没有就insert数据。当前在CTPersistanceTable以及他的类目中没找到提供该功能的API,暂时打算自己写一个CTPersistanceTable+Merge的类目来实现。
希望大神后续有空能更新出该API。如果现有的API能解决这个需求,又不影响性能的话,也请不吝赐教。
多谢!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.