Sqlite的数据本地化tips


实现数据本地化的步骤

1..设计模型

  根据实际的业务需求,设计出model层。这个就不用多说了,直接上代码。

@property(strong,nonatomic)NSString *group_id;//分组Id

@property(strong,nonatomic)NSString*group_name;//分组名称

@property(strong,nonatomic)NSString*groupOperator;//操作人

@property(strong,nonatomic)NSString*create_time;//创建时间

@property(strong,nonatomic)NSString*doctor_id;//医生id

@property(strong,nonatomic)NSNumber*isSelected;

@property(strong,nonatomic)NSNumber*sort;//序号

@property(strong,nonatomic)NSString*timestasp;//序号

 

2创建数据库文件

创建数据库文件一般有两种方法,一是通过数据库管理工具创建数据库文件,创建、设计表。我用的是sqlitemanger,也可以用其他的工具如火狐插件等,大同小异。

另一种方式是用代码创建sqlite文件,创建设计表。其实上边的工具就是封装了这些代码,实现了数据库的设计,免去了写数据库设计代码的过程。

这里我用的是第一种方法。

3.将创建的sqlite文件从bundle拷贝到沙盒的doument下,这里我用到了第三方库fmda。这里为了方便,把对数据库文件的操作封装到sqliteHelper文件里。这里我把sqlitHelper作为一个单列,把FMDatabase作为该单例的一个属性,可以看做是一个单例。你会问为什么要做成单例呢?(后边具体说)

@property(nonatomic,retain,readonly)FMDatabase*db;//数据库

+ (SqliteHelper *)sharedSqliteHelper

{

    static SqliteHelper *sharedSqliteHelper;

   

    @synchronized(self)

    {

        if(!sharedSqliteHelper)

           sharedSqliteHelper = [[SqliteHelper alloc] init];

        returnsharedSqliteHelper;

    }

}

 

- (id)init

{

    self = [super init];

    if (self) {

        //初始化数据库

        if ([self initializeDb]) {

            db = [FMDatabase databaseWithPath:dbFilePath];

        };

    }

    return self;

}

 

下边这段代码涉及到应用版本更新时数据库表结构发生改变的情况,咱们后边再说。

}- (BOOL) initializeDb {    

    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);

    NSString *documentFolderPath =[searchPaths objectAtIndex: 0];

    //查看文件目录

   NSLog(@"%@",documentFolderPath);

    dbFilePath = [documentFolderPathstringByAppendingPathComponent:@"e_doctor_db.sqlite"];

   //END:code.DatabaseShoppingList.findDocumentsDirectory

   //START:code.DatabaseShoppingList.copyDatabaseFileToDocuments

    //数据库文件是否存在Documents路径先 不存在则复制,存在则核对版本

    if (! [[NSFileManagerdefaultManager] fileExistsAtPath: dbFilePath]) {

        // didn't find db, need to copy

        NSString *backupDbPath =[[NSBundle mainBundle] pathForResource:@"e_doctor_db"ofType:@"sqlite"];

        if (backupDbPath == nil) {

            // couldn't find backup db tocopy, bail

            return NO;

        }

        else {

            BOOL copiedBackupDb =[[NSFileManager defaultManager] copyItemAtPath:backupDbPath toPath:dbFilePatherror:nil];

            if (! copiedBackupDb) {

                // copying backup db failed, bail

                return NO;

            }

            else

            {

                 [self  updateDBVersion];

            }

        }

    }

    else

    {

        //存在则核对版本 检查是否有更新

        NSString *dbverson=[selfgetCurrentDBVersion];

        if ([dbversondoubleValue]!=api_ios_db_verson) {

            //版本号不一致

            //删除数据库文件 重新复制一下数据库

            if ([[NSFileManagerdefaultManager] removeItemAtPath:dbFilePath error:nil]) {

                //删除成功 重新复制一下数据库

                NSString *backupDbPath =[[NSBundle mainBundle] pathForResource:@"e_doctor_db"ofType:@"sqlite"];

                

                if (backupDbPath == nil) {

                    // couldn't find backup db tocopy, bail

                    return NO;

                }

                else {

                    BOOL copiedBackupDb =[[NSFileManager defaultManager] copyItemAtPath:backupDbPath toPath:dbFilePatherror:nil];

                    if (! copiedBackupDb) {

                        // copying backup db failed, bail

                        return NO;

                    }

                    else

                    {

                        [self  updateDBVersion];

                    }

                }

            }

            else

            {

                NSLog(@"数据库删除失败”);

                return NO;

            }

        }

    }

    return YES;

}

3.咱们开始写dao层。

上部分截图:

@interface PatientGroupService : NSObject

/************************************************************

 方法名称:+ (NSArray *) getAllGroupsWithDoctorId:(NSString*)doctorId;

 方法描述:获取医生对应的分组列表

  数:doctorId :医生 id

 返回值:医生对应的分组列表对象的数组

 ***********************************************************/

+ (NSArray *)getAllGroupsWithDoctorId:(NSString *)doctorId;

/************************************************************

 方法名称:+ (BOOL)addGroupWithDoctorId:(PatientGroupModel *)model;

 方法描述:添加一个新分组

  数:model :分组对象

 返回值:添加是否成功 yes为成功 no为失败

 ***********************************************************/

+ (BOOL) addGroup:(PatientGroupModel *)model;

@implementation PatientService

+(BOOL)addPatient:(PatientModel *)patient

{

    BOOL result=false;

   

    //获取数据库对象,因为所以程序都用一个数据库所以都从sqliteHelper中取用

    FMDatabase *db=[SqliteHelper sharedSqliteHelper].db;

    NSString *sql=@"insert into t_patient(patient_id,telephone,name,sex,birthday,address,remark,message_number,message_updatetime,create_time,timestamp,group_id,doctor_id,allergic_history,status,certificate,medicare_card,socialsecurity_card)values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";//18

    @try {

        //打开数据库

        if ([db open]) {

            //执行插入语句获得执行结果

            result = [db executeUpdate:sql withArgumentsInArray:@[patient.pId,patient.phoneNum,patient.name,patient.sex,patient.birthDay,patient.address,[CommonHelperisBlankString:patient.headimge_url],patient.messageNum,patient.messageUpdatetime,patient.creatTime,patient.timestamp,patient.groupId,patient.doctorId,patient.allergicHistory,patient.status,patient.certificate,patient.medicare_card,patient.socialsecurity_card]];

        }

    }

    @catch (NSException *exception) {

        NSLog(@"添加病人出错:%@",exception);

    }

    @finally {

        //使用完毕之后一定要关闭数据库

        [db close];

    }

    return result;

}

+ (NSArray *)getAllGroupsWithDoctorId:(NSString*)doctorId{

    //获取数据库对象,因为所以程序都用一个数据库所以都从sqliteHelper中取用

    FMDatabase *db=[SqliteHelper sharedSqliteHelper].db;

    NSString *sql=@"selectgroup_id,group_name,create_time,operator,sort,doctor_id,timestamp fromt_patient_group where doctor_id = ? order by create_time  ";

    NSMutableArray *array=[NSMutableArray array];

    @try {

        //打开数据库

        if ([db open]) {

            //执行查询语句获得执行结果

            FMResultSet *resultset = [db executeQuery:sqlwithArgumentsInArray:@[doctorId]];

            //循环Resultset获取值

            while ([resultset next]) {

                PatientGroupModel *model=[[PatientGroupModelalloc]init];

                model.group_id=[resultset stringForColumn:@"group_id"];

                model.group_name=[resultset stringForColumn:@"group_name"];

                model.create_time=[resultset stringForColumn:@"create_time"];

                model.groupOperator=[resultset stringForColumn:@"operator"];

                model.sort=[NSNumber numberWithInt:[resultsetintForColumn:@"sort"]];

                model.doctor_id=[resultset stringForColumn:@"doctor_id"];

                model.isSelected = NO;

                model.timestasp=[resultset stringForColumn:@"timestamp"];

                //将对象添加到array

                [array addObject:model];

            }

        }

    }

    @catch (NSException *exception) {

        NSLog(@"查询病人分组出错:%@",exception);

    }

    @finally {

        //使用完毕之后一定要关闭数据库

        [db close];

    }

    return array;

}

+ (BOOL) addGroup:(PatientGroupModel*)model{

    BOOL result=false;

   

    //获取数据库对象,因为所以程序都用一个数据库所以都从sqliteHelper中取用

    FMDatabase *db=[SqliteHelper sharedSqliteHelper].db;

    NSString *sql=@"insert intot_patient_group(group_id,group_name,create_time,operator,sort,doctor_id,is_selected,timestamp)values(?,?,?,?,?,?,?,?)";

    @try {

        //打开数据库

        if ([db open]) {

            //执行插入语句获得执行结果

            result = [db executeUpdate:sql withArgumentsInArray:@[model.group_id,model.group_name,model.create_time,model.groupOperator,model.sort,model.doctor_id,[NSNumbernumberWithBool:NO],model.timestasp]];

        }

    }

    @catch (NSException *exception) {

        NSLog(@"添加病人分组出错:%@",exception);

    }

    @finally {

        //使用完毕之后一定要关闭数据库

        [db close];

    }

    return result;

    }

}上边的代码中? 和 @[]里的参数对应,多个参数用,隔开

FMResultSet *resultset =[db executeQuery:sql withArgumentsInArray:@[]]

当然也可以用stringWithFormat拼接

NSString *l = [NSStringstringWithFormat:@"selectgroup_id,group_name,create_time,operator,sort,doctor_id,is_selected,timestampfrom t_patient_group where doctor_id = %@ order by create_time",doctorId];

上边边的执行查询改为

FMResultSet *resultset =[db executeQuery:l]

程序中调用如下

[PatientGroupService addGroup:groupModel];

NSArray *arr = [PatientGroupServicegetAllGroupsWithDoctorId:@””];

1:为什么要使用单例

实际操作中你可能会出现以下的两个错误:

问题一:"is currently in use" 出现的场景是这样的,多线程操作数据库,每个线程都使用了FMDatabase实例(注意没有使用FMDatabaseQueue)。

问 题二:“database is locked"出现的场景是这样的,多线程操作数据库,每个线程各自创建了FMDatabaseQueue实例操作数据库,或者一个线程创建 FMDatabaseQueue实例来操作,而另外的线程创建了FMDatabase实例来操作。

原 因:FMDB多线程操作数据库,必须使用FMDatabaseQueue,而且必须只创建一个实例,也就是多个线程操作数据库的是同一个FMDatabaseQueue实例。首先FMDatabase是不具备线程安全的,如果两个线程中同时操作数据库,就会"iscurrently in use" ;FMDatabasequeue其实是一个调度队列(G-C-D),数据库的操作必须是顺序执行,不能两个数据库的操作同时执行,如果是两个线程各自创 建了FMDatabaseQueue的实例,线程同时执行时,就会出现相同的数据库操作同时触发,导致”database islocked“,所以必须是一个FMDatabaseQueue实例下,多个线程下同时操作,其实是在排在同一个队列中逐一操作的,没有同时操作。

注意:FMDatabaseQueue其实是在排在同一个队列中逐一操作的,并没有提高执行效率。我选用的是第一种方法,你也可以使用FMDatabaseQueue,验证下是否能够提高执行效率

解决:有两种解决方案

1. FMDatabase 作为单例,同步进行数据库操作。

2.只创建一个FMDatabaseQueue实例,所有的FMDatabase的实例方法都在这个FMDatabaseQueue实例中执行。

这里我选用了第一种解决方法。

2:数据库版本

随着软件的迭代,数据库结构也会发生改变,怎么样才能在小版本更新(数据库结构不变的情况下)尽量少的清空数据的数据。

定义一个全局宏db_version

sqlite文件里建立一个配置表

这里就有了文章开头说到的数据库版本控制,仅当数据库结构发生变化时才把该sqlite文件删除,其他情况下只对数据库里的记录修改就可以了,节省了流量。

注意:数据库版本和程序的版本是不一定是相同的,仅当数据库结构发生变化时,才修改宏的值,而且必须修改宏的值,否则会使程序的数据结构错误,照成崩溃或者数据无法写入的情况。

注意3:向数据库里写数据时记得先清楚旧的记录,否则会因为主键相同插入数据库失败,导致数据不更新的情况

 

4:利用时间戳来进一步节省流量(实际使用中有可能涉及到关系表,有些麻烦)

数据量较大的应用一般都会在本地建一个小型的数据库将服务端的一部分数据放在本地进行以提升用户体验但是问题来了以朋友表为例若数据量很大的话上万条记录你不能每次都把所有的数据都网络获取一遍浪费流量你会等待很长时间此时你可以通过时间戳来解决数据的同步问题
假设医生有ab两个客户端a客户端要和b客户端进行数据同步
解决方案如下 
     
在朋友表中加入时间戳字段服务器维护时间戳字段本地不做修改只是进行保存服务端每条记录变动时都会更新该记录的时间戳每次请求网络数据时发送朋友表中得最大时间戳服务端接收到该时间戳之后只是将大于该时间戳的记录返回给客户端首次进入的时候时发送一个最小的时间戳这样就极大的降低了传输的数据量
分析以下几种情况
1.a
客户端在1点增加了一个患者b1点后更新数据上一次更新早于1获取到朋友表中最大得时间戳该时间戳小于a增加的那条记录的时间戳这样服务器将返回a加入的这条记录将它插入b的本地数据库即可
2.a
客户端在1点更新了一个患者的信息b1点后更新数据上一次更新早于1获取到朋友表中最大得时间戳该时间戳小于a增加的那条记录的时间戳这样服务器将返回a更新的这条记录将原记录删除插入新的记录即可
3.a
客户端在1点了删除了一个患者假删除将记录状态改为删除状态b1点后更新数据上一次更新早于1获取到朋友表中最大得时间戳该时间戳小于a增加的那条记录的时间戳这样服务器将返回a更新的这条记录将原记录删除插入新的记录即可查询患者时过滤调状态为删除的部分


总上在获取到记录后根据记录的主键先删除该条记录(若为删除记录则终止),如果该记录的状态不为删除的话再重新插入数据库即可这样就实现了大量数据的同步

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值