Android持久化存储(3)SQLite数据库的使用

Grace ·
更新时间:2024-11-10
· 819 次阅读

1.什么是SQlite

SQLite是由C语言编写的一款轻型数据库,因占用资源小,处理速度快,功能齐全,特别适用于移动设备,最重要的是开源,任何人都可以使用它,许多开源项目(PHP,Python)和当今两大手机操作系统Android和iOS都使用了SQLite,造就了SQLite成为目前世界上最常见的数据库引擎。

2.SQLite特点

除了占用资源小,处理速度快等优点,SQLite还有自己的特点,那就是支持弱数据类型,其他主流SQL数据库通常支持强类型的数据,也就是每一列的类型都必须预先指定,如果输入的数据和指定数据类型对不上则报错,而SQLite采用的是弱类型,也就是说,创建一个表时指定某列的数据类型,但是你可以把任何数据类型插入该列,SQLite将检查它的类型,如果该类型与关联的列不匹配,则SQLite会尝试将该值转换成该列的类型,如果不能转换,则该值将作为其本身具有的类型存储。

SQLite支持数据类型如下:包括NULL、INTEGER、REAL、TEXT和BLOB

NULL: 该值是空值; INTEGER: 该值是一个有符号整数,根据值的大小以1、2、3、4、6或8个字节存储; REAL: 该值是一个浮点值,以8字节浮点数存储; TEXT: 该值是一个文本字符串,使用数据库编码(UTF-8,UTF-16BE或UTF-16LE)存储; BLOB: 值是BLOB数据块,以输入的数据格式进行存储。 3.Android使用SQLite

上文已说,Android集成了SQLite数据库,与此同时,Android还提供了使用SQLite数据库的API,可通过这些API可很方便的进行数据库操作。

3.1 SQLiteOpenHelper介绍

SQLiteOpenHelper是Android提供的SQLite帮助类,用于管理数据库(包括创建、增,删,改)和管理数据库的版本,方便开发者操作SQLite,SQLiteOpenHelper基本方法如下(更相信请参考官方文档https://developer.android.google.cn/reference/android/database/sqlite/SQLiteOpenHelper?hl=zh-cn)

方法名 描述
onCreate() 首次创建数据库时执行该方法,如果数据库不存在则创建,onCreate()方法在初次调用时才会被调用。重写onCreate()方法时,生成数据表结构
onUpgrade() 在需要升级数据库时调用方法,该方法检测数据库传入的版本号与当前的版本号是否相同,如果传入的版本号高于之前的版本,触发该方法
getReadableDatabase() 创建或者打开可读数据库
getWritableDatabase() 创建打开可读/写的数据库
close() 关闭所有打开的数据库对象

onCreat和onUpgrade是开发者实现,由SQLiteOpenHelper来调用。

其中onCreat是SQLiteOpenHelper在尝试找数据库时,如果没有找到则执行该方法,那么什么时候SQLiteOpenHelper去找数据库呢?是在创建SQLiteOpenHelper实例时,还是使用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()获取SQLiteDatabase时?

我们可以用代码来验证一下,我们在继承了SQLiteOpenHelper的MySQLiteOpenHelper代码里面添加一个flag作为标记,flag初始值是0,然后在构造函数里赋值成100,最后在onCreate中添加打印代码。然后我们在Activity里首先只实例化MySQLiteOpenHelper,不获取数据库,看是否触发了onCreate里的打印信息。之后再添加java SQLiteDatabase sqLiteDatabase= mySQLiteOpenHelper.getWritableDatabase();获取数据,再看打印信息。

Activity代码如下:

package com.test.sqlitedemo; import androidx.appcompat.app.AppCompatActivity; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; public class MainActivity extends AppCompatActivity { MySQLiteOpenHelper mySQLiteOpenHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mySQLiteOpenHelper=new MySQLiteOpenHelper(this,"test",1); } }

MySQLiteOpenHelper代码如下:

package com.test.sqlitedemo; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class MySQLiteOpenHelper extends SQLiteOpenHelper { private int flag=0; /** * * @param context * @param name 数据库名称 * @param version 数据库版本号 当想升级数据库时,填入大于当前版本号的数 */ public MySQLiteOpenHelper(Context context, String name, int version) { super(context, name, null, version); //这里赋值成100 flag=100; } /** * * @param db SQLiteDatabase数据库对象 */ @Override public void onCreate(SQLiteDatabase db) { Log.d("MySQLiteOpenHelper", "flag="+flag); //新建数据表 db.execSQL("create table translog(_id integer primary key autoincrement, transid , amount,date)"); flag=50; } /** * * @param db * @param oldVersion 旧版本号 * @param newVersion 新版本号 */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.i("MySQLiteOpenHelper", oldVersion + "------->" + newVersion); } }

验证结果是只实例化MySQLiteOpenHelper没有触发onCreate里的打印信息,在获取数据库的时候,SQLiteOpenHelper才会调用onCreate方法,关闭应用后再打开,也不会触发打印信息,因为数据库已经创建了。(这里验证半天,然后发现官网用一句话说清楚了什么时候创建数据库:

Create a helper object to create, open, and/or manage a database. This method always returns very quickly. The database is not actually created or opened until one of getWritableDatabase() or getReadableDatabase() is called

再来看onUpgrade方法,该方法在数据库需要升级的时候调用,当传入SQLiteOpenHelper构造版本参数大于当前的版本时,触发该方法。升级数据库是开发者经常要面对的,而且多数时候在升级数据库时不能把之前的数据全部删掉,开发者可在onUpgrade函数里添加搬运数据的代码,在触发升级后执行里面的搬运逻辑。

有人可能会问了,SQLiteOpenHelper难道仅仅是帮忙检查数据库是否存在和升级数据库用到?如果只是这两个功能,虽然能帮开发者省点工作量,但并没有省多少工作啊!别忘了一句getWritableDatabase或者getWritableDatabase就可以获得数据库实例,换成其他平台操作数据库,还得慢慢拼凑数据库连接语句,这也省了不少工作量。其他数据库降级等也可以通过这个类操作,更具体请参考官方文档。

3.2 创建数据库

了解了SQLiteOpenHelper之后,便可以开始实践数据库之旅了。创建数据库代码见上边的onCreate里的创建SQL语句。该语句创建一个名为translog的表,用来记录交易日志,translog表有4列,分别是主键id,交易单号,金额和日期,注意到除了_id设置了为integer类型(因为要设置自增长主键),其他列都没指定,执行不会报错,这是SQLite弱数据类型的好处。

db.execSQL("create table translog(_id integer primary key autoincrement, transid , amount,date)"); 3.3 添加数据

往表里添加数据有两种方法,一种使用原生SQL语句,往表里添加一条交易日志SQL语句如下

db.execSQL("INSERT INTO translog (transid, amount,date)"+ "VALUES ('955162020', 100.00,20200305)");

还有第二种也稍微简单移动的办法,那就是使用SQLiteDatabase对象的提供的insert(), update(),delete(),query()方法。这些方法其实是做了一层封装,最后还是内部转换成SQL语句完成插入数据操作。

添加数据insert(String table,String nullColumnHack,ContentValues values)有3个参数,其中:

参数1 表名; 参数2 空列的默认值; 参数3 ContentValues类型数据;

其中第二个参数nullColumnHack是空列的默认值,但又可以填null,我们知道,SQL不允许在不指定一个列名的情况下插入,例如***INSERT INTO translog ()VALUES ()")***是错的,如果第三个参数为空值,则会出现这种语法错误,第二个参数正是为了防止错误诞生,这样第三个参数为空值时,则使用第二个参数作为默认字段。那为什么又可以设置为null?这是在第三个参数values不为Null并且元素的个数大于0可以填的 。这个参数的思路是,如果你懒得检查第三个参数是否为空,那么填一个参数让我管,如果你填了一个null不让我管,那么你自己就要注意第三个参数不能为空。

第三个参数是ContentValues类型的数值,用法和map相似,提供了存取数据的put和get方法。注意键应该是表中存在的列名称。

下面是插入代码示例,注意到我们没给主键添加值,我们已经设置主键是自增长列,当没再ContentValues里给一个列put一个值时,相当于为该列添加一个null的值,主键自动增长,但是如果赋值了,则id设置成赋值后的值,建议让id自动增长,自己赋值不注意的话会导致id重复而报错。

//实例化常量值 ContentValues cValue = new ContentValues(); //添加交易单号 cValue.put("transid","9551620200307"); //添加消费金额 cValue.put("amount",100.00); //添加交易日期 cValue.put("date",2020-03-07); //调用insert()方法插入数据 db.insert("translog",null,cValue); 3.4 更新数据

更新数据当然也可用原生SQL语句然后使用execSQL() 方法可以达到目的,或者使用update()函数,函数的说明如下

int update(String table, ContentValues values, String whereClause, String[] whereArgs) 参数1 表名; 参数2 ContentValues类型数据; 参数3 WHERE表达式(String),指定需进行数据更新的行,其中?号是占位符,如果传递null将更新所有行; 参数4 填充第三个参数里的占位符,String[]数组类型;

例如想更新交易记录***9551620200307***的消费金额,可用如下代码实现

ContentValues replaceAmount = new ContentValues(); //要修改的金额 replaceAmount.put("amount","50.00"); //指定第3个参数里占位符?的内容,最后将翻译成transid=9551620200307 String[] parms=new String[] {"9551620200307"}; //执行更新 db.update("translog", replaceAmount, "transid=?", parms); 3.5 删除数据

经过熟悉增改函数后,发现函数都是有固定讨论的,删除函数delete和update函数参数很像,只不过少了要增加的参数,因为删除是破坏,破坏起来简单。

public int delete (String table, String whereClause, String[] whereArgs) 参数1 表名; 参数2 WHERE表达式(String),指定需进行删除的行,其中?号是占位符,如果传递null将删除所有行; 参数3 填充第二个参数里的占位符,String[]数组类型;

例如想删除交易记录***9551620200307***,可用如下代码实现

//指定第3个参数,也就是占位符?的内容,最后将翻译成transid=9551620200307 String[] parms=new String[] {"9551620200307"}; //执行更新 db.delete("translog", "transid=?", parms); 3.6 查询数据

查询方法放到最后,因为查询比较复杂,如果是查询全部数据还好,否则要指定各种条件。和前面的功能一样,查询也有两种方式,但要注意的是,原生查询SQL不支持db.execSQL,而是使用单独为查询设置的db.rawQuery方法,rawQuery方法两种类型,其中简单一点的原型如下:

Cursor rawQuery (String sql, String[] selectionArgs)

其中第一个参数是SQL语句,第二个参数用于填充第一个参数里的占位符。rawQuery返回Cursor,最后再通过Cursor找到想要的查询结果。示例代码如下:

Cursor cursor = db.rawQuery("select * from transid",null); while(cursor.moveToNext()){ int id=cursor.getInt(cursor.getColumnIndex("id")); String transid=cursor.getString(cursor.getColumnIndex("transid")); String amount=cursor.getString(cursor.getColumnIndex("amount")); String date=cursor.getString(cursor.getColumnIndex("date")); }

rawQuery比较适用于简单查询,如果要设置更多条件,使用这个方法将会非常复杂。这个时候建议使用query方法,虽然参数比前面介绍的数据库增删改要多得多,但要比原生SQL查询语句可读性强。

来看其中一个query原型:

Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);

参数说明

distinct:指定是否过滤结果里的重复值,true过滤,false不过滤; table:表名 columns:指定想要查询的列的名称集; selection:WHERE之后的条件语句,可以使用占位符 selectionArgs:如果selection里有占位符,使用该参数填充 groupBy:指定分组的列名对,指定的话结果将按照列名分组 having:指定分组条件,配合groupBy使用 orderBy:指定排序的列名 limit:用于限定返回结果行数

除了table必须指定,其他参数都可以设置为null,这样等同于一句简单的select * from table

结束语

SQLite因性能优秀占用体积小且开源被Android采用,如果Android程序需要保存大量数据,选择使用SQLite是一个明智的方案,Android针对SQLite封装了简单易用的API,其中SQLiteOpenHelper类用于创建和更新数据库,通过SQLiteOpenHelper,开发者不用纠结于创建数据库连接等繁琐的操作。在Android上,既可以选择使用原生SQL语句,也可以使用封装好的API操作,API包含了数据库的增删改查基本操作,通过使用API,不仅方便开发者,也增强了程序的可读性。不过,Android封装的API还是有点繁琐,目前已有大量好用的第三方库,可以更加方便操作数据库,这将会在后续介绍。


作者:风行南方



存储 sqlite数据库 SQLite Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号