概述
Sqlite数据库是Android系统内常用的数据存储的方式之一,还有其他几种存储方式:文件存储,SP存储等。
SQLite是一个进程内的轻量级嵌入式数据库,它的数据库就是一个文件,实现了自给自足、无服务器、零配置的、事务性的SQL数据库引擎。它是一个零配置的数据库,这就体现出来SQLite与其他数据库的最大的区别:SQLite不需要在系统中配置,直接可以使用。且SQLite不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite可直接访问其存储文件。
Sqlite具有如下特点:
存储结构型,关系型数据
支持SQL语言
支持事务处理
独立,无需服务进程
使用
使用SQLite实现需要了解SQLiteOpenHelper数据库工具类。
SQLiteOpenHelper是一个在Android中使用的Sqlite数据库辅助操作的工具类,可以用来管理数据库操作(增删改查)以及版本控制。
使用Sqlite数据库主要包括如下几个步骤:
创建SQLiteOpenHelper的子类,用来管理数据表的创建,数据库版本控制,根据需求实现其中的方法。
新建SQLiteDBManager数据库工具类用来承载数据库的增删改查操作。
接下来对上述两个步骤进行简单说明。
一、 SQliteOpenHelper
首先我们需要创建一个SQLiteOpenHelper的子类,作为数据库创建的辅助工具类。在此工具类中有两个重要方法:onCreate和onUpgrade方法。
onCreate是创建方法,部分表的创建可以在此实现,通过SQL语句以及execSQL()的方法可以很快的就能够实现。
onUpgrade是用来控制数据库升级的,在此方法中可以控制数据库版本的变动。
升级方法中需要对于版本的升级降级的每一项操作都要包含到,以免造成错误。
示例代码如下:
class DataBaseHelper(
context: Context?, name: String?, factory: SQLiteDatabase.CursorFactory?,
version: Int, errorHandler: DatabaseErrorHandler?
) : SQLiteOpenHelper(context, name, factory, version, errorHandler) {
override fun onCreate(db: SQLiteDatabase?) {
var sqlCreate =
"create table " + TEST_TABLE_NAME + "(" + TablePerson.ID_COLUMN + " integer primary key autoincrement," +
TablePerson.NAME_COLUMN + " varchar(64))"
db?.execSQL(sqlCreate)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
// 使用 SQL的ALTER语句
LogUtil.instance.d("onUpgrade,newVersion is $newVersion")
if (db == null)
return
db.beginTransaction() //加入事务
try {
for (j in oldVersion..newVersion) {
when (j) {
2 -> {
var sql =
"alter table $TEST_TABLE_NAME add other varchar(64)"
LogUtil.instance.d(sql)
db?.execSQL(sql)
}
3 -> {
}
else -> {
}
}
}
db.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
LogUtil.instance.d("Monicat:SQLiteDatabase upgrade failed.")
} finally {
db.endTransaction()
}
}
companion object {
const val TEST_TABLE_NAME = "person"
}
}
上面对于SQL表的操作加入和事务,使得操作具有原子性和持久性。
除此之外,还需要初始化数据库,也就是创建一个SQliteOpenHelper对象,示例如下:
/**
* 创建数据库对象
*/
fun createDb(version: Int) {
var sqLiteHelper = DataBaseHelper(
context,
"sqlite_test",
null,
version,
null
)
sqLiteDB = sqLiteHelper.readableDatabase
//和read方法一样都能够获得一个可读写的数据库对象,注意和read的区别
// sqLiteDB = sqLiteHelper.writableDatabase
}
通过SQliteOpenHelper构造函数声明数据库的版本和名称等信息,然后通过getreadableDatabase和writableDatabase获取到数据库对象,之后在通过此数据库对象进行具体的数据表操作。
需要注意的是readableDatabase和writableDatabase的区别,两者虽然都能够获取到数据库对象,但是在当数据库在磁盘满了不能写入的情况下采用writableDatabase是会打开失败报错的,而采用readableDatabase只是会以只可读的方式打开数据库,不错直接报错。
定义具体的数据类,示例如下:
package com.example.demowork1.database.sqlite
class TablePerson(
var id: Int,
var name: String
) {
companion object {
const val ID_COLUMN = "id"
const val NAME_COLUMN = "name"
}
}
二、数据操作类
在定义了数据库类之后,接下来我们就可以定义具体的数据表操作了,也就是增删改查等数据操作。
在SQLite中,数据操作有两种方式,一种是直接调用SQLiteDatabase的方法,另一种是采用执行SQL语句的方式。下面对增删改查各种操作进行逐一说明。
1. 增加
增加可以通过调用SQLiteDatabase的insert方法进行插入数据,示例如下:
/**
* 使用insert方法添加数据
* @param:插入的数据
*/
fun insert1(person: TablePerson) {
sqLiteDB?.beginTransaction() ?: return
try {
if (checkExistData(person.id)) {
LogUtil.instance.toast("数据已经存在", context)
} else {
var values = ContentValues()
values.put(TablePerson.ID_COLUMN, person.id)
values.put(TablePerson.NAME_COLUMN, person.name)
sqLiteDB?.insert(
DataBaseHelper.TEST_TABLE_NAME, null, values)
}
sqLiteDB?.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
LogUtil.instance.d("插入数据失败")
} finally {
sqLiteDB?.endTransaction()
}
}
除此之外,还可以通过调用SQL语句的方法实现
/**
* 使用SQL语句添加数据
* @param person:插入的数据
* 注意需要在字符串添加''
*/
fun insert2(person: TablePerson) {
sqLiteDB?.beginTransaction() ?: return
try {
if (checkExistData(person.id)) {
LogUtil.instance.toast("数据已经存在", context)
} else {
var insertSql =
"insert into " + DataBaseHelper.TEST_TABLE_NAME + " (" + TablePerson.ID_COLUMN + " , " +
TablePerson.NAME_COLUMN + ") values (" + person.id + " , '" + person.name + "')"
sqLiteDB?.execSQL(insertSql)
}
sqLiteDB?.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
LogUtil.instance.d("插入数据失败")
} finally {
sqLiteDB?.endTransaction()
}
}
通过SQL语句进行操作一般都是去调用execSQL方法。
2. 更新
更新可以通过调用SQLiteDatabase的update方法进行插入数据,示例如下:
/**
* 使用update方法更新数据
* @param:更新的数据
*/
fun update1(person: TablePerson) {
sqLiteDB?.beginTransaction() ?: return
try {
if (checkExistData(person.id)) {
// a. 创建一个ContentValues对象
var values = ContentValues()
values.put(TablePerson.NAME_COLUMN, person.name)
// b. 调用update方法修改数据库:将id=1 修改成 name = zhangsan
sqLiteDB?.update(
DataBaseHelper.TEST_TABLE_NAME,
values,
TablePerson.ID_COLUMN + "=?",
arrayOf(person.id.toString())
)
// 参数1:表名(String)
// 参数2:需修改的ContentValues对象
// 参数3:WHERE表达式(String),需数据更新的行; 若该参数为 null, 就会修改所有行;?号是占位符
// 参数4:WHERE选择语句的参数(String[]), 逐个替换 WHERE表达式中 的“?”占位符;
} else {
LogUtil.instance.toast("数据不存在", context)
}
sqLiteDB?.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
LogUtil.instance.d("更新数据失败")
} finally {
sqLiteDB?.endTransaction()
}
}
当然也可以通过调用SQL语句进行实现:
/**
* 使用SQL语句更新数据
* @param:更新的数据。
* 注意字符串添加''
*/
fun update2(person: TablePerson) {
sqLiteDB?.beginTransaction() ?: return
try {
if (checkExistData(person.id)) {
// 注:也可采用SQL语句修改
var updateSql =
"update " + DataBaseHelper.TEST_TABLE_NAME + " set " + TablePerson.NAME_COLUMN +
" = '" + person.name + "' where " + TablePerson.ID_COLUMN + " = " + person.id
sqLiteDB?.execSQL(updateSql)
} else {
LogUtil.instance.toast("数据不存在", context)
}
sqLiteDB?.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
LogUtil.instance.d("更新数据失败")
} finally {
sqLiteDB?.endTransaction()
}
}
3. 删除
更新可以通过调用SQLiteDatabase的delete方法进行插入数据,示例如下:
/**
* 使用delete方法删除数据
* @param:删除数据的ID
*/
fun delete1(id: Int) {
sqLiteDB?.beginTransaction() ?: return
try {
if (checkExistData(id)) {
sqLiteDB?.delete( //参数和update方法相似
DataBaseHelper.TEST_TABLE_NAME, TablePerson.ID_COLUMN + "=?",
arrayOf(id.toString())
)
} else {
LogUtil.instance.toast("数据不存在", context)
}
sqLiteDB?.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
LogUtil.instance.d("更新数据失败")
} finally {
sqLiteDB?.endTransaction()
}
}
当然也可以通过SQL语句实现:
/**
* 使用SQL语句删除数据
* @param:删除数据的ID
*/
fun delete2(id: Int) {
sqLiteDB?.beginTransaction() ?: return
try {
if (checkExistData(id)) {
// 注:也可采用SQL语句修改
var deleteSql =
"delete from " + DataBaseHelper.TEST_TABLE_NAME + " where " +
TablePerson.ID_COLUMN + " = " + id
sqLiteDB?.execSQL(deleteSql)
} else {
LogUtil.instance.toast("数据不存在", context)
}
sqLiteDB?.setTransactionSuccessful()
} catch (e: Exception) {
e.printStackTrace()
LogUtil.instance.d("更新数据失败")
} finally {
sqLiteDB?.endTransaction()
}
}
4. 查询
由于查询操作相对增删改操作更加复杂,尤其是在查询的条件和格式上都比较复杂,因此使用的SQL语句来实现查询操作,调用SQLiteDatabase的rawQuery或者query方法来实现结果。
query和rawQuery方法更多的是在参数上的不同,查看源码可以发现二者最后调用的是同一个方法。
fun queryTest1(id: Int) {
val result =
sqLiteDB?.rawQuery("select * from person where id>?", arrayOf(id.toString())) ?: return
// var result = sqLiteDB?.query(DataBaseHelper.TEST_TABLE_NAME, arrayOf(TablePerson.ID_COLUMN, TablePerson.NAME_COLUMN),
// "id>?", arrayOf(id.toString()), null, null, null) ?: return
result.moveToFirst()
while (!result.isAfterLast) {
var mId: Int = result.getInt(0)
var mName: String = result.getString(1)
LogUtil.instance.d("id=$mId name$mName")
// do something useful with these
result.moveToNext()
}
result.close()
}
可以看到查询的方式主要是游标移动查询,游标的方法有多种,部分示例如下,大家可以根据情况自行选择:
/**
//Cursor对象常用方法如下:
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); //返回当前行指定列的值
// 方法说明
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
// 参数说明
// table:要操作的表
// columns:查询的列所有名称集
// selection:WHERE之后的条件语句,可以使用占位符
// groupBy:指定分组的列名
// having指定分组条件,配合groupBy使用
// orderBy指定排序的列名
// limit指定分页参数
// distinct可以指定“true”或“false”表示要不要过滤重复值
*/
其他