数据存储与访问之——SQLite数据库

SQLite数据库,和其他的SQL数据库不同, 我们并不需要在手机上另外安装一个数据库软件,Android系统已经集成了这个数据库。

SQLite有什么特点

SQlite通过文件来保存数据库,一个文件就是一个数据库,数据库中又包含多个表格,表格里又有 多条记录,每个记录由多个字段构成,每个字段有对应的,每个值我们可以指定类型,也可以不指定 类型(主键除外)

为什么要用SQLite:

SP是一种轻量级数据存储方式,存储一些跟账号密码个人信息相关的数据,如果数据繁杂这时候就要用到SQLite存储以提高数据存取得效率。

几个相关的类:

SQLiteOpenHelper

:抽象类,我们通过继承该类,然后重写数据库创建以及更新的方法, 我们还可以通过该类的对象获得数据库实例,或者关闭数据库!

SQLiteDatabase:数据库访问类:我们可以通过该类的对象来对数据库做一些增删改查的操作

Cursor:游标,有点类似于JDBC里的resultset,结果集!可以简单理解为指向数据库中某 一个记录的指针!可以通过Cursor对数据进行一行一行查询的操作

 

使用SQLiteOpenHelper类创建数据库与版本管理

安卓给我们提供了SQLiteOpenHelper的两个方法, onCreate( )与onUpgrade( )来实现

onCreate(database)

:首次使用软件时生成数据库表

onUpgrade(database,oldVersion,newVersion)

:在数据库的版本发生变化时会被调用, 一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的,假设数据库现在的 版本是1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望 更新用户手机里的数据库表结构,为了实现这一目的,可以把原来的数据库版本设置为2 或者其他与旧版本号不同的数字即可!

 

 

public class MyDBOpenHelper extends SQLiteOpenHelper {
public MyDBOpenHelper(Context context, String name, CursorFactory factory,
int version) {super(context, "my.db", null, 1); }
@Override
//数据库第一次创建时被调用
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE person(personid INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20))");

}
//软件版本号发生改变时调用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ALTER TABLE person ADD phone VARCHAR(12) NULL");
}
}

 

我们会创建这个my.db的文件,并且会执行onCreate()里的方法, 创建一个Person的表,他又两个字段,主键personId和name字段;接着如我我们修改db的版本 号,那么下次启动就会调用onUpgrade()里的方法,往表中再插入一个字段!另外这里是插入 一个字段,所以数据不会丢失,如果是重建表的话,表中的数据会全部丢失(下面数据库更新解决)

 

流程:

Step 1:自定义一个类继承SQLiteOpenHelper类

Step 2:在该类的构造方法的super中设置好要创建的数据库名,版本号

Step 3:重写onCreate( )方法创建表结构

Step 4:重写onUpgrade( )方法定义版本号发生改变后执行的操作

使用Android提供的API操作SQLite

 

db = myDBHelper.getWritableDatabase();
switch (v.getId()) {
case R.id.btn_insert:
ContentValues values1 = new ContentValues();
values1.put("name", "呵呵~" + i);
i++;
//参数依次是:表名,强行插入null值得数据列的列名,一行记录的数据
db.insert("person", null, values1);
Toast.makeText(mContext, "插入完毕~", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_query:
sb = new StringBuilder();
//参数依次是:表名,列名,where约束条件,where中占位符提供具体的值,指定group by的列,进一步约束
//指定查询结果的排序方式
Cursor cursor = db.query("person", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
int pid = cursor.getInt(cursor.getColumnIndex("personid"));
String name = cursor.getString(cursor.getColumnIndex("name"));
sb.append("id:" + pid + ":" + name + "\n");
} while (cursor.moveToNext());
}
cursor.close();
Toast.makeText(mContext, sb.toString(), Toast.LENGTH_SHORT).show();
break;
case R.id.btn_update:
ContentValues values2 = new ContentValues();
values2.put("name", "嘻嘻~");
//参数依次是表名,修改后的值,where条件,以及约束,如果不指定三四两个参数,会更改所有行
db.update("person", values2, "name = ?", new String[]{"呵呵~2"});
break;
case R.id.btn_delete:
//参数依次是表名,以及where条件与约束
db.delete("person", "personid = ?", new String[]{"3"});
break;
}

 

 

使用SQL语句操作数据库

 

不想用Android提供的这些API, 你可以直接使用SQLiteDatabase给我们提供的相关方法:

execSQL(SQL,Object[]):使用带占位符的SQL语句,这个是执行修改数据库内容的sql语句用的

rawQuery(SQL,Object[]):使用带占位符的SQL查询操作 另外前面忘了介绍下Curosr这个东西以及相关属性,这里补充下: ——

Cursor:对象有点类似于JDBC中的ResultSet,结果集!使用差不多,提供一下方法移动查询结果的记录指针:

move(offset):指定向上或者向下移动的行数,整数表示向下移动;负数表示向上移动!

moveToFirst():指针移动到第一行,成功返回true,也说明有数据

moveToLast():指针移动到最后一样,成功返回true;

moveToNext():指针移动到下一行,成功返回true,表明还有元素!

moveToPrevious():移动到上一条记录

getCount( )获得总得数据条数

isFirst():是否为第一条记录

isLast():是否为最后一项

moveToPosition(int):移动到指定行

使用代码示例:

//插入数据
public void save(Customer customer){
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
sqLiteDatabase.execSQL("INSERT INTO customer(customerName,deliveryPhone) values(?,?)",new String[]{customer.getCustomerName(),customer.getDeliveryPhone()});
}
//删除数据
public void delete(Integer customerid){
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
sqLiteDatabase.execSQL("DELETE FROM customer WHERE customerid=?",new Integer[]{customerid});
}

//更新数据
public void updata(Customer customer){
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
sqLiteDatabase.execSQL("UPDATA customer SET customerName=?,deliveryPhone=? WHERE customerid=?",new String[]{customer.getCustomerName(),customer.getDeliveryPhone(), String.valueOf(customer.getCustomerId())});
}
//查询数据

public Customer select(Integer customerid){
SQLiteDatabase sqLiteDatabase=dbHelper.getReadableDatabase();
Cursor cursor=sqLiteDatabase.rawQuery("SELECT * FROM customer WHERE customerid=?",new String[]{customerid.toString()});
//存在数据才返回true
if(cursor.moveToFirst()){
int id=cursor.getInt(cursor.getColumnIndex("customerid"));
String name=cursor.getString(cursor.getColumnIndex("customerName"));
String phone=cursor.getString(cursor.getColumnIndex("deliveryPhone"));
return new Customer(id,name,phone);
}
cursor.close();
return null;
}
//.数据分页查询
public List<Customer> getScrollData(int offset, int maxResult)
{
List<Customer> person = new ArrayList<Customer>();
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM person ORDER BY personid ASC LIMIT= ?,?",
new String[]{String.valueOf(offset),String.valueOf(maxResult)});
while(cursor.moveToNext())
{
int id = cursor.getInt(cursor.getColumnIndex("personid"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String phone = cursor.getString(cursor.getColumnIndex("phone"));
person.add(new Customer(id,name,phone)) ;
}
cursor.close();
return person;
}
//查询记录数
public long getCount()
{
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT COUNT (*) FROM customer",null);
cursor.moveToFirst();
long result = cursor.getLong(0);
cursor.close();
return result;
}

 

除了上面获取条数的方法外还可以使用cursor.getCount()方法获得数据的条数, 但是SQL语句要改改!比如SELECT * FROM customer;

 

SQLite事务

多个操作捆绑在一起,只有所有操作都执行完毕,事务才会生效,如果其中有一个操作未执行完毕,之前所有操作都会撤销。

 

方法:

beginTransaction():开启事务

endTransaction():结束事务

setTransactionSuccessful():结束事务有两张方式,事务回滚或者事务 提交,默认为false撤销,如果提交设置为true.

简单点说就是:写在事务里的所有数据库操作都成功,事务提交,否则,事务回滚到原始状态

SQLite存储大二进制文件

一般我们很少往数据库中存储大二进制文件,比如图片,音频,视频等,对于这些我们一般 是存储文件路径,但总会有些奇葩的需求。以图片为例子,将图片保存到SQLite中,以及读取SQLite中的图片!

 

1.保存图片到Sqlite中:

(1)创建数据库表的时候,需要创建一个BLOB的字段,用于存储二进制值。

sqLiteDatabase.execSQL("CREATE TABLE test(_id INTEGER PRIMARY KEY AUTOINCREAMENT,head_img BLOB)");

(2)将图片转换成BLOB格式(这里是ImageView为例,如果是普通图片只需要转换成Bitmap再调用即可)

try {
SQLiteDatabase sqLiteDatabase=dbHelper.getWritableDatabase();
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
//压缩为PNG格式,100标示跟原图大小一致
(BitmapDrawable)imageView.getDrawable().getBitmap().compress(Bitmap.CompressFormat.PNG,100,byteArrayOutputStream);
Object[] objects=new Object[]{byteArrayOutputStream.toByteArray()};
sqLiteDatabase.execSQL("INSERT INTO test(head_img) values(?)",objects);
byteArrayOutputStream.close();
sqLiteDatabase.close();
} catch (IOException e) {
e.printStackTrace();
}

 

读取SQLite中的图片

//读取SQLIte中的图片
SQLiteDatabase sqLiteDatabase=dbHelper.getReadableDatabase();
Cursor cursor=sqLiteDatabase.rawQuery("SELECT head_img FROM text",null);
if(cursor!=null){
if(cursor.moveToFirst()){
//取出图片保存到字节数组中
byte[] img=cursor.getBlob(cursor.getColumnIndex("head_img"));
//将图片显示到Imageview上面
if(img!=null){
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(img);
imageView.setImageDrawable(Drawable.createFromStream(byteArrayInputStream,"img"));
}
}
cursor.close();
}

 

数据库升级

假如我们已经升级到第三个版本了,我们在第二个版本增加了一个表,

然后第三个版本也增加了一个表,加入用户直接从第一个版本升级到第三个版本,这样

没经过第二个版本,就没有增加的那个表,这可怎么破?

 

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
upgrade(db, oldVersion, newVersion);
}

private void upgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {
if (oldVersion + 1 == newVersion) {
switch (oldVersion) {
case 1:
upgrade1_2(db);
break;
case 2:
upgrade2_3(db);
break;
case 3:
upgrade3_4(db);
case 4:
upgrade4_5(db);
case 5:
upgrade5_6(db);
case 6:
upgrade6_7(db);
case 7:
upgrade7_8(db);
case 8:
upgrade8_9(db);
default:
break;
}
return;
}
upgrade(db, oldVersion, newVersion - 1);
upgrade(db, newVersion - 1, newVersion);
}
}

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

推荐阅读更多精彩内容

  • 一、环境 安卓系统:4.2 操作系统:Win 8.1 工具:Android Studio 二、SQLite操作 新...
    谷鸽不爱吃稻谷阅读 555评论 0 2
  • LZ-Says:给大家推荐一个网站,有兴趣可以查阅,想为大家贡献一点自己的力量也可以投稿,老大审核通过会发表,更好...
    静心Study阅读 875评论 0 3
  • 不知道我要说什么,就是想说点什么压抑在心里的一些东西。从来不想表现出来,可是一些东西发现只会越埋越深,越埋越重。 ...
    JeanWang阅读 239评论 0 1
  • 1、你若继续做过去的事,你将一直是过去的你。以自我为中心的人所拆毁的,以他人为中心的人可以重建。忍耐不仅是忍受困难...
    有长进阅读 374评论 5 1
  • shiro作为安全框架,提供了各种安全策略,此处记录身份认证Authentication的简单搭建步骤: (1)创...
    0d7bc1a914a4阅读 231评论 0 0