使用 SQLite 本地数据库

为存储数据到数据库,首先需要定义数据库结构并打开数据库。 Android 提供了一个帮助类 SQLiteOpenHelper ,其中封装了一些存储应用数据的常用数据库操作,如 创建打开 以及 更新数据库 等。

SQLiteOpenHelper 类的常用方法

构造方法

SQLiteOpenHelper (Context context, //用来打开或创建一个数据库
                String name, //用来指定数据库文件,若为 null 则指内存中的数据库
                SQLiteDatabase.CursorFactory factory, //用来创建游标 Cursor 对象,若为 null 则使用默认的
                int version) //数据库版本号,应为从 1 开始的递增整数值。

Tips: 关于版本号:
SQLiteOpenHelper 类有着管理不同版本数据库结构的能力。如果应用当前数据库版本更老,会通过 onUpgrade(SQLiteDatabase, int, int) 来更新数据库版本;反之如果当前应用数据库版本更新,则会通过 onDowngrade(SQLiteDatabase, int, int) 对数据库版本进行降级。
因此在实际应用中,每次对数据库结构做出调整后,都应该增加版本号常量。

公开的成员方法

//关闭任何已打开的数据库对象
void close () 

//创建或打开数据库
SQLiteDatabase getReadableDatabase () 

//创建或打开一个可读、可写的数据库
SQLiteDatabase getWritableDatabase () 

//配置数据库连接
void onConfigure (SQLiteDatabase db) 

//第一次创建数据库时调用。数据库表的创建以及表的初始内容的填充 在这里完成。
void onCreate (SQLiteDatabase db) 

//数据库降级
void onDowngrade (SQLiteDatabase db, 
                int oldVersion, 
                int newVersion) 
                
//数据库升级
void onUpgrade (SQLiteDatabase db, 
                int oldVersion, 
                int newVersion) 

Tips:
一般情况下,getReadableDatabase ()getWritableDatabase () 方法返回的是同一个对象,但如果出现一些极端情况(如硬盘满了),那么调用 getReadableDatabase () 方法返回的就是一个只读的数据库。

这两种方法都会花费较长的时间,因此官方不建议从主线程(包括 ContentProvider.onCreate() )中调用。

SQLiteDatabase 类常用方法

执行 SQL 语句

/**
 * execSQL()
 * 作用: 执行一个非 SELECT 的 SQL 语句或任何其他返回数据的 SQL 语句
 * @param String: 要执行的SQL语句。不支持以分号分隔的多个语句
 * Throws SQLException: 当传入的 String 无效时
 */
void execSQL(String sql)

官方鼓励开发人员尽可能使用 SQLiteDatabase 内嵌的 insert()update() 方法来取代传统的 SQL 语句,但是博主 Carson_Ho 在相关文章中却建议多使用 SQL 语句(因为相对官方提供的方法更为简单、通用)。至于具体实际应用中使用哪种为多,我也不知道了。。这里就先挖个坑吧,等经验多了再回过头来补充我自己的观点。

插入

/**
 * insert() 
 * 作用: 向数据库中插入一行数据
 * 返回值: long 型。返回新插入的行数,如果插入过程出错则返回 -1
 * @param table: 指定表的名称
 * @param nullColumHack: 可选的; 可能是null。SQL不允许在不命名至少一个列名的
 情况下插入空行。如果您提供的values是空的,则不知道列名称,并且无法插入空行。如
 果未设置为 null ,则该 nullColumnHack 参数提供可为空的列名称的名称,以便在您
 values 为空时显式插入 null
 * @param values: ContentValues - 此映射包含行的初始列值。键应该是列名,值应该是列值
 */
long insert (String table, 
                String nullColumnHack, 
                ContentValues values)             
                
//下面语句的意思是:向名为 SQLTest 的表中插入 id = 1, name = "张三" 的记录
ContentValues value = new ContentValues();
value.put("id", 1);
value.put("name", "张三");
mDatabase.insert("SQLTest", null, value);

删除

/**
 * delete()
 * 作用: 删除数据库中的行
 * 返回值: int 型。如果传入的是 where 子句那么返回受到影响的行数,否则返回 0 。要
 删除所有行并获取总数则传入 1 作为 where 子句
 * @param table: 指定表的名称
 * @param whereClause: 删除时要应用的可选 where 子句。传递null将删除所有行
 * @param whereArgs: 如果在 where 子句中包含 ? ,它将被 whereArgs 中的值替换。
 这些值将会被绑定成为字符串类型
 */
int delete (String table, 
                String whereClause, 
                String[] whereArgs) 
                
//下面语句的意思是:从名为 SQLTest 的表中删除属性 id 值为 1 的行
mDatabase.delete("SQLTest", "id=?", new String[]{"1"});

修改

/**
 * update() 
 * 作用: 更新数据库中的行
 * 返回值: int 型。返回受影响的行数
 * @param table: 指定表的名称
  values 为空时显式插入 null
 * @param values: ContentValues - 此映射包含行的初始列值。键应该是列名,值应该是列值
 * @param whereClause: 删除时要应用的可选 where 子句。传递null将删除所有行
 * @param whereArgs: 如果在 where 子句中包含 ? ,它将被 whereArgs 中的值替换。
 这些值将会被绑定成为字符串类型
 */
int update (String table, 
                ContentValues values, 
                String whereClause, 
                String[] whereArgs)

//下面语句的意思是将名为 SQLTest 的表中, id=1 的元组的 name 值改为 "李四"
ContentValues value = new ContentValues();
value.put("name", "李四");
mDatabase.update("SQLTest", value1, "id=?", new String[]{"1"});

查询

/**
 * query()
 * 作用: 查询给定的 URL,返回 Cursor 结果集
 * 返回值: 一个 Cursor 对象
 * @param distinct: 消除相同行
 * @param table: 指定表的名称
 * @param columns: 要返回的列的列表(不建议为 null ,防止直接从内存中读取数据)
 * @param selection: 一个过滤器,声明要返回哪些行,格式化为 SQL WHERE 子句(不包
 括 WHERE 本身)。传入 null 将返回给定表的所有行
 * @param selectionArgs: 如果在 selection 中包含了 ? ,那么它将被 selectionArgs 中的值替换
 * @param groupBy: 一个过滤器,声明如何对行进行分组,格式化为 SQL GROUP BY 子句(不包
 括 GROUP BY 本身)。传入 null 将导致行不被分组。
 * @param having: 一个过滤器,声明要在游标中包含哪些行组,如果正在使用行分组,则格式化为
 SQL HAVING 子句(不包括 HAVING 本身)。传入 null 将导致包含所有行组,并且在未使用行分
 组时是必需的
 * @param orderBy: 如何对行进行排序,格式化为 SQL ORDER BY 子句(不包括 ORDER BY 本身)。
 传入 null 将使用默认排序顺序,该顺序可能是无序的
 * @param limit: 限制查询返回的行数,格式为 LIMIT 子句。传入 null 表示没有 LIMIT 子句
 */
Cursor query (boolean distinct, 
                String table, 
                String[] columns, 
                String selection, 
                String[] selectionArgs, 
                String groupBy, 
                String having, 
                String orderBy, 
                String limit)

//query() 还有其他类似结构的方法,由于是部分参数的变化,这里就不一一列出了

由于这里涉及到了 Cursor ,因此要对查询结果进行进一步的操作就需要利用 Cursor 给出的方法,这里直接列出常用方法不再做多的说明:

//下面语句的意思是:将名为 SQLTest 的表中 id=1 的 "id" 列和 "name" 列返回给游标 Cursor
Cursor c = mDatabase.query(null, "SQLTest", 
                new String[]{"id", "name"}, 
                "id=?", 
                new String[]{"1"}, 
                null, null, null);

    c.move(int offset); //以当前位置为参考,移动到指定行  
    c.moveToFirst(); //移动到第一行  
    c.moveToLast(); //移动到最后一行  
    c.moveToPosition(int position); //移动到指定行  
    c.moveToPrevious(); //移动到前一行  
    c.moveToNext(); //移动到下一行  
    c.isFirst(); //是否指向第一条  
    c.isLast(); //是否指向最后一条  
    c.isBeforeFirst(); //是否指向第一条之前  
    c.isAfterLast(); //是否指向最后一条之后  
    c.isNull(int columnIndex); //指定列是否为空(列基数为0)  
    c.isClosed(); //游标是否已关闭  
    c.getCount(); //总数据项数  
    c.getPosition(); //返回当前游标所指向的行数  
    c.getColumnIndex(String columnName); //返回某列名对应的列索引值  
    c.getString(int columnIndex); //返回当前行指定列的值

Demo

SQLTest Demo

本来想着把 Demo 放到 github 上的,但是由于代码实现起来较为容易,也没有这个必要,这里就不再放出了。

参考资源:

Android API
Android 编程权威指南 [美]Phillips,B. [美]Hardy,B. [译]王明发 (人民邮电出版社)
Android :SQLlite数据库 使用手册 -- Carson_Ho

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

推荐阅读更多精彩内容