使用SQLiteOpenHelper的正确姿势

版权声明:本文为博主原创文章,未经博主允许不得转载。

前段时间写Android用到SQLIteOpenHelper时,踩了一些小坑,仔细思考了一下,踩坑的根源在于,我没有正确的理解SQLiteOpenHelper这个类。那么,我们一起来看看SQLiteOpenHelper究竟是个什么东东,以及如何正确地使用SQLiteOpenHelper,希望可以帮助博友们理解,避免踩到了类似的坑。

本文的将首先介绍下SQLiteOpenHelper的作用,然后引申出一些在使用SQLiteOpenHelper时需要注意的事项,最后,通过一个小demo演示下SQLiteOpenHelper操作数据库。

SQLiteOpenHelper是什么

望文生义,从名字上看来,至少可以得到三个方面的信息。第一,和SQLite数据库有关。第二,和打开一个数据库有关。第三,这是一个帮助类。

当然,这些都是从名字上联想到的,难免有主观臆断之嫌,哈哈。我们来看看官方的说法:

A helper class to manage database creation and version management.

所以,官方把SQLiteOpenHelper的作用解释为:一个数据库创建和版本管理的帮助类。
引起我注意的是,类名起的和解释不是很切合。
既然是管理的帮助类,并且是管理数据库的创建和版本,那么为什么类名包含"Open"这个单词呢?为什么不叫SQLiteManageHelper,或者更准确的,SQLiteCreationAndUpgradeHelper呢?且听下文分解。

我们知道,我们可以通过sql来操纵数据库,做增删改查。但是呢,如果是我们从头开始写一遍数据库逻辑的话,其实不只sql操作。一个简单的流程是这样的:打开数据库->sql操作数据库->关闭数据库。而打开数据库,又牵涉到了不少细节。比如:

  • 这个数据库是否存在?如果不存在,就要考虑先创建数据库,创建各种数据表。
  • 这个数据库是否需要升级,比如目前的数据库结构已经不能满足我们的需求了,我们需要更改数据库的结构,这个时候就要先升级数据库,更新数据库的结构。
  • 这个数据库是否之前已经打开?一般来说,我们操作数据库结束之后,需要关闭数据库,以节约资源。但是如果我们之前打开的数据库还要继续使用,这时候再执行打开操作时,直接返回这个数据库就行了。

这些细节实现起来,还是挺繁琐的,然而这些逻辑都是套路,一招鲜吃遍天,不需要每次都从头写一遍。于是,是时候SQLiteOpenHelper出场了。

SQLiteOpenHelper把打开数据库的一系列需要考虑到的细节逻辑都封装了起来,这也是为什么包含了Open这个单词的原因了。因为SQLiteOpenHelper的主要工作就是把打开数据库的逻辑帮我们实现了。
当然,打开数据库中涉及到的一些业务方面的逻辑,这是需要我们自己去实现的,SQLiteOpenHelper把这些用相应的接口分离了出来。

  • onCreate: 创建数据库时会调用,我们需要在里面创建我们的数据表。onCreate只会调用一次,如果数据库已经存在,那么打开数据库是不会回调onCreate的。
  • onUpgrade: 升级数据库时会调用,我们可以在这里面做升级操作。关于升级,还有一点需要注意的,后面会提到。
  • onDowngrade: 降级数据库时会调用,既然存在升级的场景,也可能存在降级的场景,比如我们可能觉得新版的需求没有旧版的好,想要恢复以前的需求,这时候就需要降级处理了。
  • onOpen: 打开数据库时会调用到。和onCreate有所区别的是,onCreate只在创建数据库时会调用,而onOpen在每次打开数据库时都会调用。
  • onConfigure:配置数据库连接。比如设置外键约束等。

SQLiteOpenHelper的几个误区

引入正题,我们来看看几个使用SQLiteOpenHelper可能踩到的坑或者误区。

  1. 误区一:创建SQLiteOpenHelper对象的时候,会创建数据库或者连接数据库
    不知道用过SQLiteOpenHelper的朋友们有没有和我一样,曾经犯过这样的错误,以为创建SQLiteOpenHelper对象的时候,就会创建相应的数据库,或者连接已经存在的数据库。为什么会产生这样的误解呢?仔细想了想,根源出在构造函数上。
publicSQLiteOpenHelper(Context context, String name, CursorFactory factory,intversion,DatabaseErrorHandler errorHandler)

dbName是数据库的名字,version是数据库的版本。既然构造SQLiteOpenHelper的时候,又是传dbName,又是传version的,我就想当然的以为这时候会创建数据库并且连接上数据库了。然后打开源码发现,什么都没发生[看不下去]

publicSQLiteOpenHelper(Context context, String name, CursorFactory factory,intversion,DatabaseErrorHandler errorHandler) {
     if(version <1)thrownewIllegalArgumentException("Version must be >= 1, was "+ version);
     mContext = context;
     mName = name;
     mFactory = factory;
     mNewVersion = version;
     mErrorHandler = errorHandler;
}

SQLiteOpenHelper的构造函数里只是简单的记录下传过来的参数。
真正创建数据库的时机是,显示调用打开数据库的api才会执行。也就是getReadableDatabase()和getWriteableDatabase()。这两个方法都是调用了

privateSQLiteDatabase getDatabaseLocked(booleanwritable)

而创建数据库,配置数据库,升级数据库,打开数据库这些逻辑都是在这个方法里面实现的。

  1. 误区二:在onCreate, onOpen等回调里面调用getReadableDatabase或getWriteableDatabase
    因为前面解释得很清楚了,getReadableDatabase或getWriteableDatabase是触发(创建数据库、打开数据库、升级数据库、降级数据库、配置数据库)的入口,如果在这些回调里面,又触发了getReadableDatabase或getWriteableDatabase的话,你没看错,这变成递归调用了。
    所以,不能再onCreate, onOpen, onConfigure, onUpgrade, onDowngrade回调里面调用打开数据库的方法。
  2. 误区三:升级数据库时,只需要考虑从最近的一个版本升级到最新的版本
    这种做法是错误的。举个栗子。
    假设我们的app一共发了三个版本A,B,C,并且版本A的数据库version是1,版本B的数据库version升到了2,版本C的数据库version升到了3。
    版本B里面的数据库升级逻辑是
@Override
publicvoidonUpgrade(SQLiteDatabase sqLiteDatabase,intoldVersion,intnewVersion) {
     // 在student表里面加了一个hobby字段
}

版本C里面的数据库升级逻辑是

@Override
publicvoidonUpgrade(SQLiteDatabase sqLiteDatabase,intoldVersion,intnewVersion) {
     // 在student表里面加了一个hobby字段
}

这会导致一个很严重的问题。假如用户从app的版本A升级到版本B再升级到版本C,一切沿着我们的设想,没有问题。但是如果用户在收到版本B的更新时,拒绝了更新请求;而收到版本C的更新时,同意了更新。那么问题来了,student表里面增加了hobby字段,但少了age字段!因为用户拒绝了版本B的更新,意味着没有执行版本B里面的数据库升级逻辑。
正确的做法是这样的,在版本C里面,判断老版本,如果是1,说明是从版本A升级过来的,那么就应该添加age, hobby字段;如果是2,说明是从版本B升级过来的,那么只需要添加hobby字段,因为版本B里面已经有age字段,不需要添加了。

@Override
publicvoidonUpgrade(SQLiteDatabase sqLiteDatabase,intoldVersion,intnewVersion) {
     if(oldVersion ==1) {
          // 在student表里面加了age, hobby字段
     }elseif(oldVersion ==2) {
          // 在student表里面加了hobby字段
     }
}

长话短说,升级数据库,一定要考虑以前的所有版本,对每个版本都进行适配。
当然,有升级的场景,就有降级的场景。逻辑是类似的,降级的时候,要考虑所有的高版本,对每个高本版都进行适配。

SQLiteOpenHelper的小Demo

做了一个小demo,实现了几个小功能:

  1. 创建数据库student_db,创建数据表student
  2. 添加一行数据到student表
  3. 修改一行数据
  4. 删除一行数据
  5. 按名字查询student表
  6. 升级数据库,给student表添加一个age字段,默认值是20岁

下面是demo的下载链接,欢迎下载:
Sqlite Demo

总结

  1. SQLiteOpenHelper名字里包含Open,就是想告诉我们,SQLiteOpenHelper的主要工作就是把打开数据库的逻辑帮我们实现了。
  2. 创建SQLiteOpenHelper对象的时候,不会真正创建数据库或者连接数据库。调用getReadableDatabase或getWriteableDatabase才会触发该操作。
  3. 升级数据库,一定要考虑以前的所有版本,对每个版本都进行适配。降级的时候,要考虑所有的高版本,对每个高本版都进行适配。
  4. 在onCreate, onOpen等回调不能调用getReadableDatabase或getWriteableDatabase,会产生递归调用。

感谢您的耐心阅读,以上如果有错误的地方或者理解有失偏颇,请留言指正,谢谢~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容