避免sqlite数据库升级痛苦的小技巧

wuwuwuliao 7年前
   <p>相信所有使用sqlite做本地缓存的人,开发中不可避免的一件事情就是数据库版本迁移升级,这真的是一件很蛋疼的事情。蛋疼在哪里?</p>    <p>1、升级的逻辑,iOS里面你得自己写逻辑,比如记录数据库版本,然后比对啥的。有个基于FMDB 的 FMDBMigrationManager ,不过用起来也不是很顺手。在Android开发中,至少系统SDK还提供了相关的升级API。</p>    <p>2、当需要做迁移的时候,也有很多坑,比如说模型:</p>    <pre>  <code class="language-objectivec">@interface User : NSObject    @property (nonatomic, assign) long long userId;  @property (nonatomic, copy) NSString *name;  @property (nonatomic, assign) NSUInteger *age;    @end    // sql建表语句  CREATE TABLE "User" (       "userId" INTEGER NOT NULL,       "name" TEXT,       "age" INTEGER,      PRIMARY KEY("userId")  )</code></pre>    <p>对应这个模型,数据库里面已经有一张User表了,后面要往User里面增加属性hobby,那么对数据库中已经存在的User表要做如下修改:</p>    <pre>  <code class="language-objectivec">ALTER TABLE User ADD COLUMN hobby;</code></pre>    <p>但是遇到删除属性或者改名字啥的,那就比较蛋疼了,并没有相关的sql语句。你能做的是把数据全读出来,然后建一个新的User表,然后再写进去。</p>    <p>如何避免,或者最小化这种痛苦呢?一是在建表的时候把所有情况都考虑进去,后面不再改动model的表结构,这显然不太现实,老版总有改需求的时候。第二种是使用模糊建表方法:所谓模糊建表就是说创建模型表的时候,将数据揉成一坨丢进去,取出来的时候再解析。建表的时候,只取下面几个属性:</p>    <p>1、主键,比如上面的userId。</p>    <p>2、排序属性,比如上面User要按照年龄排序的话,最好把age属性也存下来。</p>    <p>3、数据,所有的内容打包成一个jsonData属性,存起来。</p>    <p>那么建表的语句是这样的:</p>    <pre>  <code class="language-objectivec">// sql建表语句  CREATE TABLE "User" (       "userId" INTEGER NOT NULL,       "age" INTEGER,       "jsonData" TEXT,      PRIMARY KEY("userId")  )</code></pre>    <p>jsonData从哪里来?很简单,用YYModel之类的模型框架转一遍就够了,下面是代码:</p>    <pre>  <code class="language-objectivec">// 存数据  [self.cacheDatabaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {          NSString *sql = @"INSERT OR REPLACE INTO User (userId, age, jsonData) VALUES (?, ?, ?);";          NSNumber *numUserId = [NSNumber numberWithLongLong:user.userId];          NSNumber *numAge = [NSNumber numberWithUnsignedInteger:user.age];          NSString *jsonData = [user yy_modelToJSONString];          success = [db executeUpdate:sql withArgumentsInArray:@[numUserId, numAge, jsonData]];          if (!success) {              *rollback = YES;          }      }];    // 取数据      NSMutableArray *array = [NSMutableArray array];      [self.cacheDatabaseQueue inDatabase:^(FMDatabase *db) {          NSString *sql = @"SELECT * FROM User ORDER BY age DESC";          FMResultSet *rsl = [db executeQuery:sql];          while ([rsl next]) {              NSString *jsonData = [rsl stringForColumn:@"jsonData"];              [array addObject:[User yy_modelWithJSON:jsonData]];          }          [rsl close];      }];</code></pre>    <p>这样做的坏处在于,userId和age两个数据在jsonData里面也有一份,有冗余。当然也可以在使用YYModel框架提供的方法在序列化jsonData的时候过滤掉userId和age属性,但是一般没必要这样做,因为冗余数据量很小。好处在于:</p>    <p>1、数据库升级迁移的时候,增加一个属性,或者减少一个属性,你只用改模型就好了,存数据和取数据的代码依然可以用。排序属性和主键属性一般没人会改吧?如果你的老板要你改这种基本的属性的话,那么请打死他!</p>    <p>2、用YYModel等模型框架来序列化和反序列化减少了存取数据大量繁琐的代码。</p>    <p>3、之所以存取主键和排序属性在于这是必须的,主键用来区分,排序属性用来排序,其他的属性统统归为非重要属性,丢到那一坨jsonData里面就可以了。在写创建表的sql语句时,也简化了好多,你只用写很短的几行就行了。当属性比较多的时候,再也不用像这样写了,又臭又长,又容易出错:</p>    <pre>  <code class="language-objectivec">CREATE TABLE "User" (       "userId" INTEGER NOT NULL,       "name" TEXT,       "age" INTEGER,       "sex" TEXT,       "languageScores" integer,       "mathScores" integer,       "historyScores" integer,       "class" TEXT,       "grade" TEXT,      PRIMARY KEY("userId")  )</code></pre>    <p> </p>    <p>来自:http://www.jianshu.com/p/21870732fd08</p>    <p> </p>