改造SQLitePlugin插件,用FMDB统一管理

前端之家收集整理的这篇文章主要介绍了改造SQLitePlugin插件,用FMDB统一管理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

接上篇博客

hybrid应用的database locked问题

我们的是hybrid应用,基于cordova。做第一个版本的时候没经验,原生的部分用FMDB访问数据库,WEB的部分就直接找了一个sqlitePlugin用。早期的时候,原生代码和js访问数据库都是错开的,所以没有发生问题。但是到了现在,有些场景需要2边一起访问数据库,由于sqlite不支持并发写,就产生了database locked问题

其实回头看,如果在首版本就基于FMDB自己写一个cordova插件提供给js调用是很容易的,但是到了现在再改造就麻烦了很多,因为业务代码已经大量调用sqlitePlugin的js接口,所以sqlitePlugin的js部分肯定是不能动了,不然业务代码就都得改。所以最后决定采用的方案,就是把sqlitePlugin的原生部分,重新借助FMDB实现一次。这样应用的原生代码,和cordova插件,都是用FMDatabaseQueue来管理,就不会发生锁库的现象了

改造的过程难度不大,主要就是2大块:

1、把sqlitePlugin里操作数据库代码,比如sqlite3_step之类的,用FMDB提供的API替换掉

2、通过DEBUG,弄清楚js部分和原生代码部分的参数和返回值,然后适配接口,保证正确处理js传来的参数,并返回和原来的实现一样的返回值

把所有代码读了一遍,发现其实核心部分是这个方法

  1. -(CDVPluginResult*) executesqlWithDict: (NSMutableDictionary*)options

options就是js传过来的参数,里面有sql,params,query,qid这4个key,所以接下来我们调用FMDB API所需要的参数,也是从其中取出来。而这个方法最后的返回值,必须包含qid,type,result,error,具体来说,qid是原样返回,type可能是success或error,result则必须包含rows,也就是select查询的结果(其实还有rowAffected和其他一些key,但是检查了发现没有用,所以新的实现就忽略了这些key)

上述的参数和返回值搞清楚以后,剩下的部分就是重新实现。sqlitePlugin原来的代码有1000行左右,所做的也无外乎调用sqlite3的api,还有处理类型转换,参数绑定等常规动作,这些事情在FMDB里都已经做过了,所以借助FMDB重新实现是非常简单的。改造后的代码只有200行不到,核心也是这个方法

  1. -(CDVPluginResult*) executesqlWithDict: (NSMutableDictionary*)options
  2. {
  3. NSString *sql = [options objectForKey:@"sql"];
  4. NSArray *params = [options objectForKey:@"params"];
  5. NSMutableArray *resultRows = [NSMutableArray array];// 查询结果集
  6. __block NSDictionary *error = nil;
  7. [dbHelper doOperation:^(FMDatabase *db){
  8. if([[sql lowercaseString] hasPrefix:@"select"]){
  9. FMResultSet *rs = [db executeQuery:sql withArgumentsInArray:params];
  10. if(!rs){
  11. error = @{@"code":[NSNumber numberWithInt:[db lastErrorCode]],@"message": [db lastErrorMessage]};
  12. }else{
  13. while([rs next]){
  14. [resultRows addObject:[rs resultDictionary]];
  15. }
  16. [rs close];
  17. }
  18. }else{
  19. BOOL result = [db executeUpdate:sql withArgumentsInArray:params];
  20. if(!result){
  21. error = @{@"code":[NSNumber numberWithInt:[db lastErrorCode]],@"message": [db lastErrorMessage]};
  22. }
  23. }
  24. }];
  25. if(error){
  26. return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
  27. }
  28. NSDictionary *cdvResult = @{@"rows": resultRows};
  29. return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:cdvResult];
  30. }

只要熟悉FMDB的API,上面的代码都是很直接的,没有什么需要特别说明的。

另外,这个plugin持有一个dbHelper的实例变量:

  1. {
  2. YLSDatabaseHelper *dbHelper;
  3. }
  4.  
  5. -(CDVPlugin*) initWithWebView:(UIWebView*)theWebView
  6. {
  7. self = (sqlitePlugin*)[super initWithWebView:theWebView];
  8. if (self) {
  9. dbHelper = [YLSDatabaseHelper sharedInstance];
  10. }
  11. return self;
  12. }

这个YLSDatabaseHelper是个单例,原生的代码以及这个插件,都通过这个唯一入口访问数据库,因此保证了不会并发写(FMDatabaseQueue的内部机制)。如果这个dbHelper不是单例,而是有多个实例,其实是无法避免database locked的
  1. @implementation YLSDatabaseHelper
  2.  
  3. {
  4. FMDatabaseQueue* queue;
  5. }
  6.  
  7. -(id) init
  8. {
  9. self = [super init];
  10. if(self){
  11. NSString *dbFilePath = [YLSGlobalUtils getDatabaseFilePath];
  12. queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
  13. }
  14. return self;
  15. }
  16.  
  17. +(YLSDatabaseHelper*) sharedInstance
  18. {
  19. static dispatch_once_t pred = 0;
  20. __strong static id _sharedObject = nil;
  21. dispatch_once(&pred,^{
  22. _sharedObject = [[self alloc] init];
  23. });
  24. return _sharedObject;
  25. }
  26.  
  27. +(void) refreshDatabaseFile
  28. {
  29. YLSDatabaseHelper *instance = [self sharedInstance];
  30. [instance doRefresh];
  31. }
  32.  
  33. -(void) doRefresh
  34. {
  35. NSString *dbFilePath = [YLSGlobalUtils getDatabaseFilePath];
  36. queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
  37. }
  38.  
  39. -(void) doOperation:(void(^)(FMDatabase*))block
  40. {
  41. [queue inDatabase:^(FMDatabase *db){
  42. block(db);
  43. }];
  44. }
  45.  
  46. @end
这样改造之后,对js部分完全没有影响,WEB里的业务代码一点都不需要修改。但是毕竟是改造,其实如果在首版本就能意识到这个问题,可以直接这样写一个插件,不需要引入sqlitePlugin,然后插件的js部分也可以简单很多

猜你在找的Sqlite相关文章