Android

Android数据库SQLite详解

勤劳的小蜜蜂 · 1月1日 · 2019年 · ·

SQLite概述

  • SQLite是个非常流行的嵌入式数据库,它符合 SQL-92 标准的基本要求,并且只利用很少的内存就有很好的性能。Android 运行时环境包含了完整的 SQLite,因此不需要安装,可以直接使用。
  • SQLite是一个相对完整的 SQL系统,拥有完整的触发器和事务处理等。但SQLite不支持一些标准的SQL 功能,特别是外键约束(foreign key constrains)、事务的嵌套、外连接right outer join以及全外连接full outer join, 也不支持alter table功能。
  • SQLite是一个紧凑的库。启用所有功能后,库大小可能小于600KiB,具体取决于目标平台和编译器优化设置。(64位代码更大。并且一些编译器优化,例如积极的函数内联和循环展开可能导致目标代码更大。)在内存使用和速度之间存在权衡。SQLite通常运行得越快,你给它的内存就越多。然而,即使在低内存环境中,性能通常也非常好。根据它的使用方式,SQLite可以 比直接文件系统I / O更快

数据类型

SQLite的数据类型一共只有5种:

  • null:空值。
  • integer:带符号的整型,具体取决有存入数字的范围大小。
  • real:浮点数字,存储为8-byte IEEE浮点数。
  • text:字符串文本。
  • blob:二进制对象。

SQLite 和其它数据库最大的不同就是对数据类型的支持,创建一个表时,可以在 create table 语句中指定某列的数据类型,但实际上可以把任何数据类型放入任何列中。当某个值插入数据库时,SQLite 将检查它的类型。如果该类型与对应列的类型不匹配,则 SQLite 会尝试将该值转换成该列的类型。如果不能转换,则该值将作为其本身具有的类型存储。比如可以把一个字符串(String)放入 integer 列。

创建数据库和表时,注意以下几点:

  • 所有表都应该有主键,使用关键字primary key定义,通常为自增量的整数。
  • 图像和音频文件这类文件不应存储在数据库中。
  • Android 数据库都存储在设备上的 /data/data//databases 文件夹中。默认情况下,所有数据库都是私有的,只能由创建它们的应用程序访问。

基本操作方法

由于 JDBC 会消耗太多的系统资源,所以 JDBC 对于手机这种内存受限设备来说并不合适。因此,Android 提供了一些新的 API 来使用 SQLite 数据库,这些API可以完成所有的SQL操作。

1.打开数据库

有二种打开方式,第一种是仅打开,如果数据库不存在的话会抛出异常,第二种是先判断数据库是否存在,如果不存在,先创建然后再打开。通常使用的都是第二种方法,例如:

SQLiteDatabase sqlDB=openOrCreateDatabase(DATABASE_NAME,SQLiteDatabase.CREATE_IF_NECESSARY,null);

2.执行SQL语句

SQLiteDatabase提供二个执行SQL语句的方法:execSQL()和rawQuery(),前者用于执行一条非查询SQL语句,后者执行一条查询SQL语句并返回查询的结果。

这是execSQL()的例子:

sqlDB.execSQL("insert into person(name, age) values('王五', 23)");
sqlDB.execSQL("update person set name= '李丽' where id=2");

这是rawQuery()的例子:

Cursor cursor = sqlDB.rawQuery("select * from person", null);

利用这二个方法就能完成数据表的创建、数据的增删改查等功能。

3.游标

前面的例子中出现了游标(Cursor),它是用于接受select查询的返回值,它包含了0条至多条记录的数据。rawQuery()方法会把查询的结果包装在一个Cursor的对象中返回,可以对结果集进行向前、向后或随机的访问。

常用方法说明
int getCount()返回Cursor 中的行数
int getColumnCount()返回所有列的总数
boolean moveToFirst()移动光标到第一行
boolean moveToLast()移动光标到最后一行
boolean moveToNext()移动光标到下一行
boolean moveToPrevious()移动光标到上一行
boolean moveToPosition(int position)移动光标到一个绝对的位置
boolean isAfterLast()在最后一行之后
boolean isBeforeFirst()在第一行之前
boolean isFirst()是否第一行
boolean isLast()是否最后一行
String getColumnName(int columnIndex)从给定的索引返回列名
getString(int columnIndex)从给定的索引该列的值(字符串类型)
getInt(int columnIndex)从给定的索引该列的值(整型)
getXxx(int columnIndex)从给定的索引该列的值(Xxx类型,如长整型、浮点型等)
boolean isNull(int columnIndex)该列的值是否为null
void close()关闭游标,释放资源
boolean isClosed()是否已关闭

以下是一个综合了数据库创建、数据表创建、数据的插入、删除、修改以及查询的例子:

private static String TAG = "====C06Activity====";
private static final String DATABASE_NAME = "MyContacts.db";
private static final String TABLE_NAME = "person";
// Declare an object of SQLiteDatabase class
private SQLiteDatabase sqlDB;

public void createDB(View v) {
	sqlDB = openOrCreateDatabase(DATABASE_NAME,
				SQLiteDatabase.CREATE_IF_NECESSARY, null);
	Log.v(TAG,"数据库已创建");
		
	sqlDB.execSQL("CREATE TABLE " + TABLE_NAME
				+ "(id integer primary key autoincrement, name text, age integer)");
	Log.v(TAG,"表已创建");

	sqlDB.execSQL("insert into person(name, age) values('张三', 21)");
	sqlDB.execSQL("insert into person(name, age) values('李四', 22)");
	sqlDB.execSQL("insert into person(name, age) values('王五', 23)");
	Log.v(TAG,"插入三行");
		
	sqlDB.execSQL("update person set name= '李丽' where id=2");
	Log.v(TAG,"更新一行");

	sqlDB.execSQL("delete from person where id=3");
	Log.v(TAG,"删除一行");

	Cursor cursor = sqlDB.rawQuery("select * from person", null);
	while (cursor.moveToNext()) {
		int id = cursor.getInt(0); // 获取第一列的值,第一列的索引从0开始
		String name = cursor.getString(1);// 获取第二列的值
		int age = cursor.getInt(2);// 获取第三列的值

		Log.v(TAG, "id=" + id + ", name=" + name + ", age=" + age);
	}
	cursor.close();
	sqlDB.close();
}

专用操作方法

直接通过SQL语句对数据库进行操作有许多不便,为此SQLite专门定义了一些与insert、update、delete和select有关的方法,方便了程序的编写。


public long insert (String table, String nullColumnHack, ContentValuesinitialValues);
  • table:需要插入数据的表名。
  • nullColumnHack:这个参数需要传入一个列名。SQL标准并不允许插入所有列均为空的一行数据,所以当传入的initialValues值为空或者为0时,用nullColumnHack参数指定的列会被插入值为NULL的数据,然后再将此行插入到表中。
  • initalValues:用来描述要插入行数据的ContentValues对象,即列名和列值的映射。
  • 返回值:新插入行的行id。如果有错误发生返回-1。

public int update (String table, ContentValues values, String whereClause,String[] whereArgs);
  • table:更新数据的表名。
  • values:用来描述更新后的行数据的ContentValues对象,即列名和列值的映射。whereClause,可选的where语句(不包括WHERE关键字),用来指定需要更新的行。若传入null则表中所有的行均会被更新。
  • whereArgs:where语句中表达式的?占位参数列表,参数只能为String类型。返回值:被更新的行的数量。

public int delete (String table, String whereClause, String[] whereArgs);
  • table:需要删除行的表名。
  • whereClause:可选的where语句(不包括WHERE关键字),用来指定需要删除的行。若传入null则会删除所有的行。
  • whereArgs:where语句中表达式的?占位参数列表,参数只能为String类型。
  • 返回值:若传入了正确的where语句则被删除的行数会被返回。若传入null,则会返回0。若要删除所有行并且返回删除的行数,则需要在where语句的地方传入字符串1。

public Cursor query (String table, String[] columns, String selection,String[] selectionArgs, String groupBy, String having, String orderBy,String limit);
  • table:检索的表名。
  • columns:由需要返回列的列名所组成的字符串数组,传入null会返回所有的列。selection,指定需要返回的行的where语句(不包括WHERE关键字)。传入null则返回所有行。
  • selectionArgs:where语句中表达式的?占位参数列表,参数只能为String类型。
  • groupBy:对结果集进行分组的group by语句(不包括GROUP BY关键字)。传入null将不对结果集进行分组。
  • having:对分组结果集设置条件的having语句(不包括HAVING关键字)。必须配合groupBy参数使用,传入null将不对分组结果集设置条件。
  • orderBy:对结果集进行排序的order by语句(不包括ORDER BY关键字)。传入null将对结果集使用默认的排序。
  • limit:对返回的行数进行限制的limit语句(不包括LIMIT关键字)。传入null将不限制返回的行数。
  • distinct:如果希望结果集没有重复的行传入true,否则传入false。
  • cursorFactory:使用这个CursorFactory来构造返回的Cursor子类对象,传入null使用默认的CursorFactory。
  • 返回值:指向第一行数据之前的Cursor子类对象。

在项目的SqlHelper类中,主要是采用这些方法来实现具体的增删改功能的。以下代码是实现前一例子中增删改功能的另一种实现方法:

	//sqlDB.execSQL("insert into person(name, age) values('张三', 21)");
	ContentValues values = new ContentValues();
	values.put("name","张三");
	values.put("age",21);
	sqlDB.insert("person", "id", values); // id列为空,由SQLite自动赋值 
		
	//sqlDB.execSQL("insert into person(name, age) values('李四', 22)");
	long newId = sqlDB.insert("person", "id", null); // 插入空列,然后再修改它 
	if(newId!=-1){
		values.clear();
		values.put("name","李四");
		values.put("age",22);
		sqlDB.update("person", values, "id = "+newId, null); // newId是刚插入的行的ID
	}
		
	//sqlDB.execSQL("insert into person(name, age) values('王五', 23)");
	values.clear();
	values.put("name","王五");
	values.put("age",23);
	newId = sqlDB.insert("person", "id", values); // id列为空,由SQLite自动赋值 

	Log.v(TAG,"插入三行");
		
	if(newId!=-1){
		sqlDB.delete("person","id=?",new String[] {newId+""});
		Log.v(TAG,"删除一行");
	}

	//Cursor cursor = sqlDB.rawQuery("select * from person", null);
	// 改为查询某条记录
	String[] columns = { "id", "name", "age"};
	String[] parms = { "张三" };
	Cursor cursor = sqlDB.query("person", columns, "name=?", parms, null,null, null);
	while (cursor.moveToNext()) {
		int id = cursor.getInt(0); // 获取第一列的值,第一列的索引从0开始
		String name = cursor.getString(1);// 获取第二列的值
		int age = cursor.getInt(2);// 获取第三列的值

		Log.v(TAG, "id=" + id + ", name=" + name + ", age=" + age);
	}

SQLiteOpenHelper

为了进一步提高编程效率,Android提供了SQLiteOpenHelper类。SQLiteOpenHelper 类是一个抽象类,定义了创建、打开和升级数据库的方法。程序员在开发时,需要创建一个SQLiteOpenHelper类的子类,实现其构造方法和二个抽象方法,并实现一些管理数据的方法。

详细说明见下述代码中的注释:

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class SqlDemo extends SQLiteOpenHelper {
	/**
	 * 构造方法,根据提供的数据库名,自动创建数据库。并根据数据库的版本号, 维护不同的数据库版
	 * 
	 * @param context
	 */
	public SqlDemo(Context context) {
		// 例中数据库名是mydatabase.db,数据库的版本号是1
		super(context, "mydatabase.db", null, 1);
	}

	/**
	 * 必须实现的方法,创建数据表
	 */
	@Override
	public void onCreate(SQLiteDatabase db) {
		// 根据数据结构的设计,创建数据表的语句
	}

	/**
	 * 必须实现的方法,升级数据表
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// 一种做法是删除旧数据表,直接创建新版本的数据表
		// 另一种做法是创建新数据表后将数据从旧数据表中导入到新数据表中,再删除旧数据表
	}

	/**
	 * 以下是自行添加的方法,通常必须包括对本数据库进行增删改查的方法。
	 */
	// public Cursor select() {
	// public void delete(int id) {
}
0 条回应