前言:sqlite 是在移动端作为数据cache的一种技术方式,现在在各种app使用也是极其广泛.但是,iOS自带的sqlite使用库,其api的难用性,易懂性都很让人头疼.所以,FMDB作为第三方的sqlite封装库,被广为应用.因为其面向对象的方式封装,所以其api也是很通俗易懂的.下面,我就在使用FMDB过程中需要注意的几点小问题罗列一下.这些问题,可以提高你在使用sqlite的效率,以及安全性.
目录
1. 创建表
2. 数据更新
3. 更改表结构
4. 多线程
下面从这三个方面进行阐述.
创建表
这里不谈你的表结构怎么设计.只谈你在建表的时候需要注意的几点信息.可以提升你的查询效率以及将来的可扩展性.
1.1 索引
sqlite,其广泛使用也是因为他提供了方便的关系式的存储结构,以及强大的查询功能.在建表的时候,一起把索引页创建完成,
能对你的数据查询提高很高.
NSString* messageTableIndexCreateSql = [NSString stringWithFormat:@"CREATE INDEX IF NOT EXISTS message_index ON %@(sessionid,messageid);",tableName];
[self.dataBase executeUpdate:messageTableIndexCreateSql];
1.2 扩展字段
我估计每个人在建表的时候,都会预留一个扩展字段(ext),为以后的表的扩展做预留.我这里再次提一下,其目的是为想展示一下我在这个扩展字段里存放的内容.
我在这个可扩展字段里,放了一段json结构的数据串.都可以key-value结构的.一个字段,可以扩展成N个字段使用.
数据更新
涉及到数据库操作,就设计到数据的更新.包括插入,删除,更新.
2.1 插入
插入数据需要从效率以及安全性上考虑.
效率:
首先要考虑是单条插入,还是多条插入
单条数据就直接插入就好了.对于多条数据就需要考虑以下几点.
1.这多条数据是否具有原子性,一致性,隔离性,持久性.
原子性(Atomicity):确保工作单位内的所有操作都成功完成,否则,事务会在出现故障时终止,之前的操作也会回滚到以前的状态。
一致性(Consistency):确保数据库在成功提交的事务上正确地改变状态。
隔离性(Isolation):使事务操作相互独立和透明。
持久性(Durability):确保已提交事务的结果或效果在系统发生故障的情况下仍然存在。
如果满足以上几点,那我们就需要考虑事务.(Transaction),在FMDB中提供的事务的操作.
- (BOOL)rollback;
- (BOOL)commit;
- (BOOL)beginTransaction;
举个列子,你有10条数据,如果需要这10条数据,需要同时入库.如果其中一条失败其余数据也不能入库,这样的话,就使用事务.当失败的话,rollback到最初状态.
插入数据的时候,最好不用insert.改用replace. 因为这个关键字执行的操作是delete + insert. 如果原来有数据存在,他会把原来的数据delete,然后在插入.
这样做的好处是,如果原来存在数据,你要是用insert的话,如果主键一致.会导致你insert失败.还有一个好处是,可以达到update的效果.
安全:
如果你设计的表的字段的类型是string,而且这个字段所需要插入的数据来自用户输入.满足这两个条件,你就需要考虑到sql注入了.(具体什么sql注入自行google)
怎么解决呢?
有两种方式:
1.自己把所有的要插入的sql语句统一把' 替换成 ''
[value.content stringByReplacingOccurrencesOfString:@"'" withString:@"''"]
2.调用sqlite原始的格式字符串的api.吐个槽.FMDB,没有带格式化sql的api.
sqlite3_mprintf
** Because the %q format string is used, the '\'' character in zText ** is escaped and the SQL generated is as follows: ** ** <blockquote><pre> ** INSERT INTO table1 VALUES('It''s a happy day!') ** </pre></blockquote> **
以上两种方式都可以.
更改表结构
这个话题,我想是所有的程序员都不愿意看到的.因为终端的版本一旦发出去,那就意味不能修改了.如果在产品初期,需要不定的情况,我想没有人能设计出来应变所有变化的表结构.
即使留有我上面提到的可扩充字段,也不能只靠一个字段解决所有的结构变化.
所以,在终端我的实现方式是缓存表结构.(把表的结构用文本的方式缓存到本地)每次在打开数据库的时候,先读本地的表结构,然后跟代码中写的表结构做对比.如果一点发现不一致.直接drop表.虽说有点暴力,但是影响并不是太大.最坏的结果是本地的数据没有了(没关系,服务器还有).这样,最起码保证程序能够正常运行.
多线程
sqlite本身不是线程安全的.但是FMDB提供了一个FMDatabaseQueue,这个可以解决多线程访问数据的问题.
by the way.我本身没有用这种方式.我把所有的数据访问都放在一个线程里去做,这样在不影响整体数据访问,插入效率的情况下,是一种不错的方式.
或者,还可以自己在最外部创建一个队列,管理数据的插入,删除.比如用GCD的barrier.