ios开发数据库版本迁移手动更新迭代和自动更新迭代艺术(-)

简介: demo 地址 https://github.com/PureLovePeter/DataCache 好用的话 star star star数据库版本迁移顾名思义就是在原有的数据库中更新数据库,数据库中的数据保持不变对表的增、删、该、查。

demo 地址 https://github.com/PureLovePeter/DataCache 好用的话 star star star

数据库版本迁移顾名思义就是在原有的数据库中更新数据库,数据库中的数据保持不变对表的增、删、该、查。

数据持久化存储:

  • plist文件(属性列表)

  • preference(偏好设置)

  • NSKeyedArchiver(归档)

  • SQLite 3

  • CoreData

这几种方式,我就不介绍其他的了,你可以自己去查询其功能用处,主要SQLite 3对数据的存储

一、手动更新迭代

由于开发需要需要对消息进行存储,第三方的数据库局限所以我们公司要求对聊天记录、聊天对像、好友进行查询(说白了就是和微信搜索做的一模一样就好了)。

首先你要明白一点为了避免在不通的线程中对相同的数据库进行操作fmdb已经对数据库加锁了。我在数据库中使用了单例,保证工程全局都可以使用。

+ (MyFMDB *)sharedMyFMDB {

    static dispatch_once_t once;

    static id instance;

    dispatch_once(&once, ^{

        instance = [self new];

    });

   return instance;

}

在本篇文章中先说一下大致的数据库迁移的思路,在数据库中存储一个记录版本号的表

Version表。本地写一个记录工程需要不要更新的版本静态变量

#define kCurrentSqliteVersion 0;发布新版本的时候就更改本地的数据库版本号 0 ->1 然后从数据库中取出数据库中的版本号0。

    if (oldSqliteVer < kCurrentSqliteVersion) {//sqlite版本小于当前要创建的版本,需要更新

    }

 

依次类推

 [self upgrade:oldSqliteVer];    //更新数据库内容

 [self insertSqliteVersion:kCurrentSqliteVersion];   //版本号更新需要放在更新数据库内容之后,在没有版本号的数据库版本中,需要在upgrade的地方去创建version表

 然后用递归的方式更新

- (void)upgrade:(NSInteger)oldVersion {

    if (oldVersion >= kCurrentSqliteVersion) {

        return;

    }

    switch (oldVersion) {

        case 0:

            [self upgradeFrom0To1];

            break;

        case 1: //从1版本升级到2版本

            [self upgradeFrom1To2];

            break;

        case 2: //版本拓展:以后若有增加则持续增加

            [self upgradeFrom2To3];

            break;

        case 3: //版本拓展:以后若有增加则持续增加

            [self upgradeFrom3To4];

            break;

        default:

            break;

    }

    oldVersion ++;

    // 递归判断是否需要升级:保证老版本从最低升级到当前

    [self upgrade:oldVersion];

}

举个例子,在对应的方法里边写自己更新的内容就可以了。

- (void)upgradeFrom1To2 {

    //这里执行Sql语句 执行版本1到版本2的更新

    FMDatabase * db = [FMDatabase databaseWithPath:self.dbPath];

    NSNumber *userId = [NSNumber numberWithLongLong:[UserManeger shareInstance].currentUser.uid];

    if ([db open]) {

        NSString* SysMessageSql = [NSString stringWithFormat:@"alter table SysMessage add userId int default %@",userId];

        NSString* importSql = [NSString stringWithFormat:@"alter table User add userId int default %@",userId];

        NSString* importChatSql = [NSString stringWithFormat:@"alter table UserChatMessage add userId int default %@",userId];

        BOOL resSysMessage = [db executeUpdate:SysMessageSql];

        BOOL res = [db executeUpdate:importSql];

        BOOL resChat = [db executeUpdate:importChatSql];

        

        if (res&&resSysMessage&&resChat) {

            NSLog(@"更新User,UserChatMessage,SysMessage字段成功");

        }else{

            NSLog(@"更新User,UserChatMessage,SysMessage字段失败");

        }

       [db close];

    }

}

 

 

一、数据库的自动更新迭代 

数据库自动更新顾名思义就是:

1.数据库中有的不需要的表删除、需要的没有的表的增加

2.对数据库已有的不需要的字段删除、需要的没有的字段增加;

 代码详解:

说到自动更新迭代不得不提及runtime,扫盲一下--因为runtime可以获得属性的类型和属性。利用这一点特性进行数据库的自动更新迭代。(由于公司要做数据缓存,才写了这个自动更新换代的数据库)

首先和手动更新迭代一样创建单例数据库对象,全局调用

//创建CacheFMDB类的对象

static CacheFMDB* _instance = nil;

+ (instancetype)sharedCacheFMDB

{

    static dispatch_once_t onceToken ;

    dispatch_once(&onceToken, ^{

        _instance = [[self alloc] init] ;

    }) ;

    return _instance ;

}

我们首先要想怎么拼接数据库语句?

CREATE TABLE PositionTable (id integer PRIMARY KEY NOT NULL , isCollection int , isMyPosition int , isPublicPosition int , newAnswer int , statusId int , importantPos int , matchRate float , urgency int , positionType int , grabOrderPositionStatus int , productType int , lastUpdateTime double , uid double , annualSalary double , areaName text , cityId double , companyId double , createTime double , positionLogo text , companyName text , positionId double , maxShowAnnualSalary double , minShowAnnualSalary double , modifyTime double , publishTime double , suitableTalentCount double , positionTitle text , updateTime double , positionAveFeedBackDay text )

像上边这一个创建数据库语句的方法,

REATE TABLE %@ (id integer PRIMARY KEY NOT NULL 

这些是固定的然后呢后边是不通的属性和对应的类型拼接而成。

 

那么c语言对应的oc语言的类型

static NSString *intType     = @"i"; // int_32t,int

static NSString *longlongType = @"q"; // long,或者longlong

static NSString *floatType   = @"f"; // float

static NSString *doubleType  = @"d"; // double

static NSString *boolType    = @"B"; // bool

static NSString *imageType   = @"UIImage"; // UIImage 类型

static NSString *stringType  = @"NSString"; // NSString 类型

static NSString *numberType  = @"NSNumber"; // NSNumber 类型

 用model便利每一个属性的变量名

    Ivar *ivars = class_copyIvarList([model class], &count);

 我们发现变量名字都多余了一个"_"。包括oc对应的储存的数据库语句。所以我进行进一步处理

for (int i = 0; i < count; i++) {

        Ivar ivar = ivars[i];

        //根据ivar获得其成员变量的名称

        const char *name = ivar_getName(ivar);

        //C的字符串转OC的字符串

        NSString *key = [NSString stringWithUTF8String:name];

        //放入数组

        NSString *keyString = [key stringByReplacingOccurrencesOfString:@"_" withString:@""];

        [_ivarsArray addObject:keyString];

        // 获取变量类型,c字符串

        const char *cType = ivar_getTypeEncoding(ivar);

        //C的字符串转OC的字符串

        NSString *Type = [NSString stringWithUTF8String:cType];

        //基本类型数组库类型转化

        NSLog(@"======%@",Type);

        NSString *repleaceString = [self repleaceStringWithCSting:Type];

        //放入数组

        [_typeArray addObject:repleaceString];

    }

 

/*****属性和数据库数据的类型相互转换*****/

- (NSString *)repleaceStringWithCSting:(NSString *)cSting{

    if (![cSting isEqualToString:@""]) {

        if ([cSting isEqualToString:@"i"]) {

            return @"int";

        }else if([cSting isEqualToString:@"q"]){

            return @"double";

        }else if([cSting isEqualToString:@"f"]){

            return @"float";

        }else if([cSting isEqualToString:@"d"]){

            return @"double";

        }else if([cSting isEqualToString:@"B"]){

            return @"int";

        }else if([cSting containsString:@"NSString"]){

            return @"text";

        }else if([cSting containsString:@"NSNumber"]){

            return @"long";

        }

        NSAssert(1, @"handleSqliteTable类中 model的属性状态不对导致数据库状态不对,请核对后再拨");

        return @"未知";

    }else return nil;

}

 

准备工作就绪

我在处理数据库语句的时候写了个外部调用方法

//

//  handleSqliteTable.h

//  RuntimeDemo

//

//  Created by peter on 16/3/18.

//  Copyright © 2016年 hunteron All rights reserved.

//

 

#import <Foundation/Foundation.h>

 

@interface handleSqliteTable : NSObject

 

/**

 *  获取 model 中的所有属性数组

 *  model      需要缓存的对象

 */

- (NSArray *)ivarsArrayWithModel:(NSObject *)model;

 

/**

 *  返回 数据库表的语句

 *  tableName  表名字

 *  model      需要缓存的对象

 */

- (NSString *)sqliteStingWithTableName:(NSString *)tableName model:(NSObject *)model;

 

/*

 *获取属性的类型,并转化为c的类型,并进行拼接

 */

- (NSArray *)attribleArray:(NSArray *)attribleArray model:(NSObject *)model;

 

@end

 

 

 

-----------------------数据库语句的拼接.m文件

//数据库拼接

- (NSString *)sqliteStingWithTableName:(NSString *)tableName model:(NSObject *)model{

    

    [self test1:model];

    

    return  [self complatSqiteAttribiteA:_ivarsArray typeA:_typeArray tableName:tableName];

}

 

- (NSString *)complatSqiteAttribiteA:(NSArray *)attribiteA typeA:(NSArray *)typeA tableName:(NSString *)tableName{

    NSString *string = [NSString stringWithFormat:@"CREATE TABLE %@ (id integer PRIMARY KEY NOT NULL",tableName];

    NSString *beginString = @"";

    for (int i = 0; i < attribiteA.count;i ++) {

        NSString *atAndType = [self sqiteStringAttribite:(NSString *)attribiteA[i] type:(NSString *)typeA[i]];

        beginString = [beginString stringByAppendingString:atAndType];

 

    }

    return [NSString stringWithFormat:@"%@ %@)",string,beginString];

}

 

/**

 *  数据库语句拼接

 */

- (NSString *)sqiteStringAttribite:(NSString *)attribite type:(NSString *)type{

    return [NSString stringWithFormat:@", %@ %@ ",attribite,type];

}

 

 

你把要转化的model通过

/**

 *  返回 数据库表的语句

 *  tableName  表名字

 *  model      需要缓存的对象

 */

- (NSString *)sqliteStingWithTableName:(NSString *)tableName model:(NSObject *)model;

 传过来,例如:

    NSString * talentSql = [handel sqliteStingWithTableName:NSStringFromClass([talent class]) model:talent];

 就会生成对应的数据库语句。

CREATE TABLE PositionTable (id integer PRIMARY KEY NOT NULL , isCollection int , isMyPosition int , isPublicPosition int , newAnswer int , statusId int , importantPos int , matchRate float , urgency int , positionType int , grabOrderPositionStatus int , productType int , lastUpdateTime double , uid double , annualSalary double , areaName text , cityId double , companyId double , createTime double , positionLogo text , companyName text , positionId double , maxShowAnnualSalary double , minShowAnnualSalary double , modifyTime double , publishTime double , suitableTalentCount double , positionTitle text , updateTime double , positionAveFeedBackDay text )

 

这才是第一步的数据库语句的拼接,是不是到这一步感觉已经好麻烦了??????没事不要着急

 

数据库的迁移无非是:

1.新增数据库(没有路径的情况)

2.新增表

3.增加字段

4.删除字段(sqlit3不支持字段的删除)

 一、判断数据库路径是否存在

    NSFileManager * fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:kCacheDBPath]) {

        这里我就不说了

    }else if([self needUpdateTabelArray:_modelArray]) {

        NSLog(@"数据库已经存在路径,但需要新增数据库表:%@",kCacheDBPath);

        //需要更新的表的名字

        NSArray *tabelName = [self needUpdateTable:_modelArray];

        //新建数据表

        [self createNewTable:tabelName];

    }else{

        NSLog(@"数据库已经存在路径,不需要新增数据库表:%@",kCacheDBPath);

    }

二、新增表

我会创建一个数组去记录表名字

    _modelArray = [NSMutableArray array];

    handleSqliteTable *handel = [[handleSqliteTable alloc]init];

     //职位列表

    PositionTable *position = [[PositionTable alloc]init];

    [_modelArray addObject:position];

    NSString * positionSql = [handel sqliteStingWithTableName:NSStringFromClass([position class]) model:position];

    

    //人才列表

    TalentTable *talent = [[TalentTable alloc]init];

    [_modelArray addObject:talent];

    NSString * talentSql = [handel sqliteStingWithTableName:NSStringFromClass([talent class]) model:talent];

 判断有没有这个表数据库中

//多个表是不是要更新

- (BOOL)needUpdateTabelArray:(NSArray *)array{

    for (NSObject *obj in array) {

        NSString *string = NSStringFromClass([obj class]);

        NSLog(@"=====%d",[self needUpdateTabel:string]);

        if(![self needUpdateTabel:string]) {

            return YES;

        }

    }

    return NO;

}

 

//检查数据库的表是否存在

- (BOOL)needUpdateTabel:(NSString *)tableName{

    FMDatabase * db = [FMDatabase databaseWithPath:kCacheDBPath];

    BOOL need = NO;

    if ([db open]) {

        //得到所有的表表名

        FMResultSet *rs = [db executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableName];

        

        while ([rs next])

        {

            // just print out what we've got in a number of formats.

            NSInteger count = [rs intForColumn:@"count"];

            NSLog(@"isTableOK %ld", (long)count);

            

            if (0 == count)

            {

                need = NO;

            }

            else

            {

                need = YES;

            }

        }

        [rs close];

        [db close];

    }

    return need;

}

表不存在就

//新增表

- (void)createNewTable:(NSArray *)array{

    for (NSObject *obj in _modelArray) {

        NSString *string = NSStringFromClass([obj class]);

        if ([array containsObject:string]) {

            handleSqliteTable *handel = [[handleSqliteTable alloc]init];

            NSString * tableSqilet = [handel sqliteStingWithTableName:NSStringFromClass([obj class]) model:obj];

            [self createATable:tableSqilet];

        }

    }

}

 检查表里边的字段需不需要更新,去现在有的model的属性和数据库中的比较找出需要更新的字段

 //自动更新机制 表的对应的model变化,需要对表做相应的增加和删除操做

    for (NSObject *obj in _modelArray) {

        //runtime 获取现有model的所有属性string

        NSArray *array = [handel ivarsArrayWithModel:obj];

        //对比数据库和现有的属性sting

        [self checkAndUpdateTable:obj newAttribe:array];

        

    }

 

//判断新老表中有没有新增字段

- (void)checkAndUpdateTable:(NSObject*)objName newAttribe:(NSArray *)newAttribe{

    //数据库中现有的字段

    NSMutableArray *sqliteArray = [NSMutableArray array];

    NSString *tableName = NSStringFromClass([objName class]);

    FMDatabase * db = [FMDatabase databaseWithPath:kCacheDBPath];

    if ([db open]) {

        NSString * sql = [NSString stringWithFormat:@"select * from %@",tableName] ;

        FMResultSet * rs = [db executeQuery:sql];

        NSDictionary * dict =   [rs columnNameToIndexMap];

        [sqliteArray addObjectsFromArray:[dict allKeys]];

        [db close];

    }

    //需要更新的字段

    NSMutableArray *needUpdateName =[NSMutableArray array];

    for (NSString *string in newAttribe) {

        NSString * lowercaseString = [string lowercaseString];

        if (![sqliteArray containsObject:lowercaseString]) {

            [needUpdateName addObject:string];

        }

    }

    handleSqliteTable *handel = [[handleSqliteTable alloc]init];

    if (needUpdateName.count > 0) {

        NSArray *array = [handel attribleArray:needUpdateName model:objName];

        //更新

        [self updateTabelupdateString:array tableName:tableName];

    }

}

//增加新的表字段

- (void)updateTabelupdateString:(NSArray *)updateArray tableName:(NSString *)tableName{

    

    FMDatabase * db = [FMDatabase databaseWithPath:kCacheDBPath];

    if ([db open]) {

        for (NSString *updateString in updateArray) {

            NSString* SysMessageSql = [NSString stringWithFormat:@"alter table %@ add %@",tableName,updateString];

            BOOL resSysMessage = [db executeUpdate:SysMessageSql];

            if (resSysMessage) {

                NSLog(@"新增%@表字段%@成功",tableName,updateString);

            }else{

                NSLog(@"新增%@表字段%@失败",tableName,updateString);

            }

        }

        [db close];

    } 

}

这就完成了9成的更新功能。

还有一成更新表的增删改查语句。同样的道理更新数据库语句.......

 

相关文章
|
21天前
|
存储 关系型数据库 数据挖掘
【瑶池数据库动手活动及话题本周精选(体验ADB、 SelectDB,参与 RDS 迁移训练营)】(4.21-4.27)
本文为 “瑶池数据库动手活动及话题精选” 系列第一期,聚焦 SelectDB 日志分析、AnalyticDB Zero-ETL 集成、RDS 迁移训练营三大实战,设积分、实物等多重奖励,同步开启话题互动。点击链接参与,每周解锁数据库实战新场景。
|
1月前
|
SQL Oracle 关系型数据库
用 YashanDB Migration Platform,数据库迁移不再是“高风险动作”
数据库迁移一直是企业信息化中的难题,耗时长、风险高。YashanDB Migration Platform(YMP)提供一站式解决方案,涵盖评估、改写、迁移与校验全流程。其核心能力包括SQL自动适配、智能对象迁移、高性能数据通道及数据对比校验,显著降低人力成本与业务风险。适合从Oracle、MySQL等迁移到YashanDB的企业,以及需异构整合或国产化替代的集团、政府和国企项目。YMP不仅是工具,更是推动数据库国产化的关键平台。
|
15天前
|
存储 关系型数据库 OLAP
【瑶池数据库动手活动及话题本周精选(体验ADB、 SelectDB,参与 RDS 迁移训练营)】(4.28-5.4)
本文为“瑶池数据库动手活动及话题精选”系列第二期,聚焦SelectDB日志分析、AnalyticDB Zero - ETL集成、RDS迁移训练营三大实战,设积分、实物等多重奖励,同步开启话题互动。上一期活动反响热烈,错过的朋友别再犹豫!点击链接参与,每周解锁数据库实战新场景,抓紧时间,精彩不容错过!
|
1月前
|
SQL 调度 数据库
开发YashanDB数据库?用 DBeaver for YashanDB 更顺手
数据库开发复杂易错,尤其在企业级场景中。为提升效率,YashanDB 团队基于 DBeaver 开源工具打造专属解决方案——DBeaver for YashanDB。它支持多类型数据库对象管理(表、视图、函数等),适配 YashanDB 特有表结构(HEAP、LSC),提供智能补全、语法高亮、SQL 调试等功能,让开发更高效流畅。推荐用于数据库应用开发团队、高频调试用户及中大型企业统一工具栈场景。
|
1月前
|
SQL 数据可视化 IDE
开发数据库不想写命令?YashanDB Developer Center 帮你轻松搞定
YashanDB Developer Center(YDC)是一款可视化的数据库开发工具,专为提升数据库开发效率而设计。它通过图形化对象管理让数据库对象清晰可见,提供智能SQL编辑器支持语法高亮与自动补全,实现PL调试的图形化操作,帮助快速定位问题。此外,操作记录可追溯,多端灵活部署,适配多种场景。无论是中大型企业研发团队,还是不熟悉命令行的业务开发者,YDC都能显著优化开发体验,堪称YashanDB的“可视化IDE”。
|
2月前
|
物联网 测试技术 API
时序数据库 InfluxDB 3.0 版本性能实测报告:写入吞吐量提升效果验证
TSBS 测试表明,对于少于 100 万台设备的数据集,InfluxDB OSS 3.0 的数据写入速度实际上比 InfluxDB OSS 1.8 更慢。 对于 100 万台及以上设备的数据集,InfluxDB OSS 3.0 的数据写入性能才开始超过 InfluxDB OSS 1.8。 InfluxDB OSS 3.0 的数据写入接口与 InfluxDB 1.8 并不兼容,用户无法顺利迁移。
81 7
|
2月前
|
SQL Oracle 关系型数据库
担心YashanDB异构数据库迁移踩“坑”?听听大咖们怎么说
文章围绕异构数据库迁移展开,探讨了避免数据丢失、保障数据完整性、注意兼容性、提升迁移效率、做好反向演练等问题。包括迁移前完整性检查与备份,YashanDB 从内核设计和配套工具保障数据,对兼容性进行大量测试,通过合理评估和技术手段提升迁移效率,以及处理回退等内容。
|
2月前
|
Oracle 关系型数据库 网络安全
崖山异构数据库迁移利器YMP初体验-Oracle迁移YashanDB
文章是作者小草对崖山异构数据库迁移利器 YMP 的初体验分享,包括背景、YMP 简介、体验环境说明、YMP 部署(含安装前准备、安装、卸载、启动与停止)、数据迁移及遇到的问题与解决过程。重点介绍了 YMP 功能、部署的诸多细节和数据迁移流程,还提到了安装和迁移中遇到的问题及解决办法。
|
2月前
|
SQL 关系型数据库 MySQL
MySQL生产环境迁移至YashanDB数据库深度体验
这篇文章是作者将 MySQL 生产环境迁移至 YashanDB 数据库的深度体验。介绍了 YashanDB 迁移平台 YMP 的产品相关信息、安装步骤、迁移中遇到的各种兼容问题及解决方案,最后总结了迁移体验,包括工具部署和操作特点,也指出功能有优化空间及暂不支持的部分,期待其不断优化。

热门文章

最新文章