Android 数据存储之SQLite

SQLite数据存储

SQLite时一款轻量级的关系型数据库,运算速度非常快,支持SQL标准语法,还遵循ACID事务。

SQLiteOpenHelper用法

Android提供了一个SQLiteOpenHelper帮助类,借助这个类可以快捷的对数据库进行创建和升级。
由于SQLiteOpenHelper是一个抽象类,所以我们要创建一个他的子类来实现它的方法。SQLiteOpenHelper有两个抽象方法:onCreate()onUpgrade(),创建的类中必须重写这两个方法,分别在方法里实现创建和升级数据库的逻辑。

创建数据库

public class DBHelper extends SQLiteOpenHelper{
    public static final String TABLE_NAME = "Book";
    public static final String KEY_BOOK_AUTHOR = "author";
    public static final String KEY_BOOK_PRICE = "price";
    public static final String KEY_BOOK_PAGES = "pages";
    public static final String KEY_BOOK_NAME = "name";
    public static final String CREATE_BOOK =
            "create table " + TABLE_NAME + " ("
            + "id integer primary key autoincrement, " +
                    KEY_BOOK_AUTHOR + " text, " +
                    KEY_BOOK_PRICE + " real, " +
                    KEY_BOOK_PAGES + " integer, " +
                    KEY_BOOK_NAME + " text)"
            ;


    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
    }
}

代码分析:
首先我们利用SQL语法,实现一个创建指定表名称的建表语句TABLE_NAME

数据类型:integer表示整形,real表示浮点型,text表示文本类型,blob表示二进制类型。主键关键词:primary key,并且用autoincrement表示主键是自增长的(初始值为1,递增1)。

有了建表语句,我们在onCreate()方法内处理一些创建表的逻辑。SQLiteDataBase.execSQL(String)执行一个简单的SQL语句,也就是我们刚才创建的建表语句。

那么我们是何时去调用onCreate()来创建我们的表呢?这里SQLiteOpenHelper还有另外两个重要的方法:getReadableDatabase()getWritableDatabase()。这两个方法都可以创建或者打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。

getReadableDatabase()getWritableDatabase()区别:在数据库不可写入时(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则将出现异常。这两个方法在正常情况下都是对数据库有读写功能。

在得到SQLiteOpenHelper实例后,再调用getWritableDatabase()或者getReadableDatabase()方法来创建数据库。同时onCreate()也会得到执行,创建数据库中的表。如果数据库已经创建,就不会再去执行onCreate(),而是打开现有的数据库。如下图:

打开数据库.png

升级数据库

升级数据库则需要用到onUpgrade()。我们在创建一个自定义的SQLiteOpenHelper类时,有一个表示当前版本号的参数。如果我们传入比第一次创建的数据可版本号大的数,就可以让onUpgrade()执行,实现更新数据库。

@Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
            case 1:
                //db.execSQL(create new table);
            case 2:
                //db.execSQL(alter table);
        }
    }

代码分析:首先在更新之前判断旧版本号,对上之后执行相应的更新语句。这里有一点要注意,不要加break;这么做的好处是即便是很老的数据库更新到最新的,会按照顺序执行每一个中间版本的更新

操作数据库的表

通过调用getWritableDatabase()或者getReadableDatabase()方法来创建数据库同时,还会返回一个SQLiteDatabase对象。借助这个对象就可以对数据进行CRUD(增删改查)。

添加数据

SQLiteDatabase对象提供了insert()方法,用于向指定的表添加数据。他有三个参数:

  • 表名:用于指定哪张表添加数据
  • 默认值:用于在未指定添加数据情况下给某些可为空的列自动赋值为null
  • ContentValues:ContentValues对象提供了一系列的putXXX()重载方法,向ContentValues中添加数据。
ContentValues values = new ContentValues();
            values.put(DBHelper.KEY_BOOK_AUTHOR, book.getAuthor());
            values.put(DBHelper.KEY_BOOK_PAGES, book.getPages());
            values.put(DBHelper.KEY_BOOK_PRICE, book.getPrice());
            values.put(DBHelper.KEY_BOOK_NAME, book.getName());
            mDatabase.insert("Book", null, values);
            values.clear();

代码分析:mDatabase对象就是SQLiteDatabase实例。

当我们需要一次性添加多个数据时,可以在每次添加下一个数据前调用ContentValues.clear()来清除之前put的数据。

添加数据.png

更新数据

SQLiteDatabase对象提供了update()方法,用于向指定的表更新指定的数据。他有四个参数:

  • 表名:用于指定哪张表更新数据
  • ContentValues:ContentValues对象提供了一系列的putXXX()重载方法,向ContentValues中添加数据。组装更新数据
  • selection:相当于SQL语句的WHERE子句。结合第四个参数确定更新哪一行数据。指定null时更新所有行。注意要使用占位符"?"
  • selectionArgs:提供的字符串数组为第三个参数中的每个占位符指定相应的内容。

更新一行的数据时,并不需要指定每一列的数据,只需要组装需要更新的列和新数据。

更新数据.png

删除数据

SQLiteDatabase对象提供了update()方法,它有三个参数:

  • 表名:用于指定哪张表删除数据
  • selection:相当于SQL语句的WHERE子句。结合第四个参数确定更新哪一行数据。指定null时删除所有行。注意要使用占位符"?"
  • selectionArgs:提供的字符串数组为第三个参数中的每个占位符指定相应的内容。
删除数据.png

查询数据

SQLiteDatabase对象提供了update()方法,最简单的一种重载方法,接受七个参数:

  • 表名:用于指定哪张表查询数据
  • 列名:指定查询哪几列,不指定的话查询所有列
  • selection:相当于SQL语句的WHERE子句。结合第四个参数确定更新哪一行数据。指定null时查询所有行。注意要使用占位符"?"
  • selectionArgs:提供的字符串数组为第三个参数中的每个占位符指定相应的内容。
  • groupBy:指定需要groupBy的列,不指定表示对查询结果不进行groupBy、操作
  • having:对groupBy后的数据进行进一步过滤,不指定表示不进行过滤
  • orderBy:对查询结果排序,不指定使用默认排序方式

调用update()返回一个Cursor对象,查询结构都从这个对象取出。

Cursor cursor = mDatabase.query(DBHelper.TABLE_NAME,
                columns,
                selection,
                args,
                null,
                null,
                null);
        if(cursor.moveToFirst()) {
            do{
                buffer.append(cursor.getString(cursor.getColumnIndex(columns[0])) + "|");
                buffer.append(cursor.getDouble(cursor.getColumnIndex(columns[1])) + "|");
                buffer.append(cursor.getInt(cursor.getColumnIndex(columns[2])) + "|");
                buffer.append(cursor.getString(cursor.getColumnIndex(columns[3])) + "\n");
            }while (cursor.moveToNext());
        }
        cursor.close();

代码分析:得到Cursor对象以后,首先调用他的moveToFirst()方法将数据的指针移动到第一行。如果数据集为空,返回false。通过Cursor的getColumnIndex()获取某一列载表中对应的位置索引,然后将这个索引传入到对应类型的取值方法中,获取每一行的数据。获取完一行数据后,调用moveToNext()移动光标到下一行,如果到了最后一行,返回false。读取完整个查询结果以后,调用close()关闭光标,释放所有资源并且将Cursor引用置为null。

查询.png

使用SQL操作数据库

对于增删改的操作我们可以选择以下方式来操作:execSQL()

db.execSQL("insert into Book (name, author,pages,price) values(?,?,?,?)",
new String[]{"name2", "author2", "pages2", "price2"});

但是查询数据只能用以下方法:rawQuery()

db.rawQuery("select * from Book", null);

使用事务

事务的特性可以保证让某一系列的操作要么全部完成,要么一个都不会完成。例如有一种情况,更新旧的数据库,需要删除所有的数据,并且替换新的数据。我们要保证的是,删除和添加新数据必须一起完成,否则保留原始数据。

SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();//开启事务
try{
    db.delete("Book", null, null);
    ContentValues v = new ContentValus();
    if(true) {
        throw new NullPointerException();//手动抛出异常,让事务失败
    }
    v.put("name", "Game of Thrones");
    v.put("author", "George Martin");
    v.put("pages", 720);
    v.put("price", 20.85);
    db.insert("Book", null, v);
    db.setTransactionSuccessful();//事务已经执行成功
}catch (Exception e) {
    e.printStackTrace();
}finally{
    db.endTransaction();//结束事务
}

这里我们抛出一个异常来模拟不能预料的意外,这样一来添加新数据的代码无法执行,此时事务起到了作用,保留了原始旧数据。

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

推荐阅读更多精彩内容