前言
对于一个应用程序而言,数据持久化是必不可少的,Android程序也不例外。这篇博客将介绍Android中关于SQLite的使用,SQLite是一种嵌入式的数据库引擎,专门适用于资源有限的设备上进行适量的数据存储,而Android就全面支持标准的SQLite数据库。在本片博客中,将说明SQLite数据库的创建以及维护,还有使用SQLite执行CRUD的两种方式,以及SQLite中事务的使用,最后都会使用示例讲解博客中所提到的概念性的内容。
SQLite
Android对SQLite数据库,提供了完全的支持,而所有创建的SQLite数据库,仅限于当前应用访问,如果其他应用需要访问,则必须提供的Content Provider的支持,并且SQLite数据库会随着Android应用的卸载而被删除。SQLite是一个嵌入式的数据库引擎,最后是以文件的形式保存数据的。从本质上来看,SQLite的操作方式只是一种更为便捷的文件操作,当应用程序创建或打开一个SQLite数据库时,其实只是打开一个文件准备读写。因为SQLite仅适用于资源有限的小型设备,所以本身就不应该把大量数据存储在设备的SQLite数据库里,SQLite只适合存储一些小型的数据。
为了使SQLite和其他数据库间的兼容性最大化,SQLite支持对列上类型进行“类型近似”,列的类型近似指的是存储在列上的数据进行推荐类型存储。所以虽然SQLite内部只支持NULL、INTEGER、REAL(浮点书)、TEXT(文本)和BLOB(大二进制对象)这五种数据类型,但实际上SQLite完全可以接受varchar(n)、char(n)、decimal(p,s)、date等类型数据,只不过SQLite会在运算或保存时将它们转换为上面五种数据类型中相应的类型。大多数数据库的引擎都是使用静态的、强类型的数据类型,数据的类型是由它的容器决定的,这个容器是指被存放的特定列。而SQLite使用的是动态类型,在SQLite中,值的数据类型跟值本身相关,而不是与它的容器相关,所以SQLite允许把各种类型的数据保存到任何类型字段中,开发者可以不用关心声明该字段说使用的数据类型。但是有一种情况例外,定义为INTEGER PRIMARY KEY的字段只能存储64位整数,当向这种字段保存除整数意外的其他类型的数据时,SQLite会产生错误。
SQLite数据库创建与维护
从官方文档上了解到,在Android项目中,创建SQLite数据库推荐继承SQLiteOpenHelper类,然后重写其中的onCreate()方法,在onCreate()方法中,对执行数据库创建的SQL语句。而SQLiteOpenHelper不仅仅用于SQLite数据的创建,还可以对其进行维护,以及获得SQLiteDatabase这个数据库操作对象。
SQLiteOpenHelper提供了两个构造器,用于传递当前上下文对象以及SQLite数据库版本信息,在SQLiteOpenHelper的继承类的构造函数中,会调用它,构造器的签名如下:
- SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version).
- SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactroy factory,int version,DatabaseErrorHandler errorHandler).
上面的构造函数中,都是用于创建一个SQLite数据库,context为一个当前应用的上下文对象;name是数据库名称;factory是一个允许子类在查询时使用的游标,一般不用传Null;version是数据库版本号;errorHandler是一个接口,传递当数据库错误的时候,执行的补救方法。
在SQLiteOpenHelper中,可以进行SQLite数据库的创建、维护、日志以及获取可读写的数据库对象,通过下面几个常用方法得到支持:
- String getDatabaseName():获取数据库名。
- SQLiteDatabase getReadableDatabase():创建或者打开一个可读的数据库对象。
- SQLiteDatabase getWritableDatabase():创建或者打开一个可读/写的数据库对象。
- abstract void onCreate(SQLiteDatabase db):当第一次调用SQLiteOpenHelper的时候执行,之后再次调用将不再执行,一般用于完成数据库初始化的工作。
- void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion):当数据库版本号发生向上更新时,被执行。
- void onDowngrade(SQLiteDatabase db,int oldVersion,int newVersion):当数据库版本号发生向下更新时,被执行。
下面提供一个简单的SQLiteOpenHelper的继承类代码,用于创建数据库以及表结构:
1 package com.example.sqlitedbdemo.db;
2
3 import android.content.Context;
4 import android.database.sqlite.SQLiteDatabase;
5 import android.database.sqlite.SQLiteOpenHelper;
6
7 public class DbOpenHelper extends SQLiteOpenHelper {
8 private static String name = "mydb.db";
9 private static int version = 1;
10
11 public DbOpenHelper(Context context) {
12 super(context, name, null, version);
13 }
14
15 @Override
16 public void onCreate(SQLiteDatabase db) {
17 // 只能支持基本数据类型
18 String sql = "create table person(id integer primary key autoincrement,name varchar(64),address varchar(64))";
19 db.execSQL(sql);
20 }
21 @Override
22 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
23 // TODO Auto-generated method stub
24 String sql="alter table person add sex varchar(8)";
25 db.execSQL(sql);
26 }
27 }
Tips:当创建好SQLite数据库的之后,可以在/data/data/<package name>/databases目录下找到SQLite数据库文件。
执行CRUD操作
当使用SQLiteOpenHelper的getReadableDatabase()或者getWritableDatabase()方法获取到SQLiteDatabase对象,就可以对这个数据库进行操作了。
对于熟悉SQL语句的开发者而言,其实只需要使用两个方法,即可执行所有CRUD操作,以下方法提供多个重载方法:
- void execSQL():通过SQL语句执行一条非查询语句。
- Cursor rawQuery():通过SQL语句执行一条查询语句。
下面以一个示例讲解一下单纯使用SQL语句实现CRUD操作:
接口代码:
1 package com.examle.sqlitedbdemo.service;
2
3 import java.util.List;
4 import java.util.Map;
5
6 public interface PersonService {
7
8 public boolean addPerson(Object[] params);
9 public boolean deletePerson(Object[] params);
10 public boolean updatePerson(Object[] params);
11 public Map<String, String> viewPerson(String[] selectionArgs);
12 public List<Map<String, String>> listPersonMaps(String[] selectionArgs);
13 }
接口的实现代码:

1 package com.examle.sqlitedbdemo.dao;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import android.content.Context;
9 import android.database.Cursor;
10 import android.database.sqlite.SQLiteDatabase;
11
12 import com.examle.sqlitedbdemo.service.PersonService;
13 import com.example.sqlitedbdemo.db.DbOpenHelper;
14
15 public class PersonDao implements PersonService {
16 private DbOpenHelper helper = null;
17
18 public PersonDao(Context context) {
19 helper = new DbOpenHelper(context);
20 }
21
22 @Override
23 public boolean addPerson(Object[] params) {
24 boolean flag = false;
25 SQLiteDatabase database = null;
26 try {
27 // insert一条数据
28 String sql = "insert into person(name,address,sex) values(?,?,?)";
29 database = helper.getWritableDatabase();
30 // 执行SQL
31 database.execSQL(sql, params);
32 flag = true;
33 } catch (Exception e) {
34 e.printStackTrace();
35 } finally {
36 if (database != null) {
37 // finally中关闭数据库
38 database.close();
39 }
40 }
41 return flag;
42 }
43
44 @Override
45 public boolean deletePerson(Object[] params) {
46 boolean flag = false;
47 SQLiteDatabase database = null;
48 try {
49 // 删除一条数据
50 String sql = "delete from person where id=?";
51 database = helper.getWritableDatabase();
52 database.execSQL(sql, params);
53 flag = true;
54 } catch (Exception e) {
55 e.printStackTrace();
56 } finally {
57 if (database != null) {
58 database.close();
59 }
60 }
61 return flag;
62 }
63
64 @Override
65 public boolean updatePerson(Object[] params) {
66 boolean flag = false;
67 SQLiteDatabase database = null;
68 try {
69 // 更新一条数据
70 String sql = "update person set name=?,address=?,sex=? where id=?";
71 database = helper.getWritableDatabase();
72 // 执行SQL
73 database.execSQL(sql, params);
74 flag = true;
75 } catch (Exception e) {
76 e.printStackTrace();
77 } finally {
78 if (database != null) {
79 database.close();
80 }
81 }
82 return flag;
83 }
84
85 @Override
86 public Map<String, String> viewPerson(String[] selectionArgs) {
87 Map<String, String> map = new HashMap<String, String>();
88 SQLiteDatabase database = null;
89 try {
90 // 查询单条记录
91 String sql = "select * from person where id=?";
92 // 以只读的形式打开数据库
93 database = helper.getReadableDatabase();
94 // 执行SQL语句,返回一个游标
95 Cursor cursor = database.rawQuery(sql, selectionArgs);
96
97 int colums = cursor.getColumnCount();
98 while (cursor.moveToNext()) {
99 for (int i = 0; i < colums; i++) {
100 String cols_name = cursor.getColumnName(i);
101 String cols_value = cursor.getString(cursor
102 .getColumnIndex(cols_name));
103 if (cols_value == null) {
104 cols_value = "";
105 }
106 map.put(cols_name, cols_value);
107 }
108 }
109 } catch (Exception e) {
110 e.printStackTrace();
111 } finally {
112 if (database != null) {
113 database.close();
114 }
115 }
116 return map;
117 }
118
119 @Override
120 public List<Map<String, String>> listPersonMaps(String[] selectionArgs) {
121 List<Map<String, String>> list = new ArrayList<Map<String, String>>();
122 String sql = "select * from person";
123 SQLiteDatabase database = null;
124 try {
125 database = helper.getReadableDatabase();
126 Cursor cursor = database.rawQuery(sql, selectionArgs);
127 int colums = cursor.getColumnCount();
128 while (cursor.moveToNext()) {
129 Map<String, String> map = new HashMap<String, String>();
130 for (int i = 0; i < colums; i++) {
131 String cols_name = cursor.getColumnName(i);
132 String cols_value = cursor.getString(cursor
133 .getColumnIndex(cols_name));
134 if (cols_value == null) {
135 cols_value = "";
136 }
137 map.put(cols_name, cols_value);
138 }
139 list.add(map);
140 }
141 } catch (Exception e) {
142 e.printStackTrace();
143 } finally {
144 if (database != null) {
145 database.close();
146 }
147 }
148 return list;
149 }
150 }

再写一个测试类测试这个数据操作类是否有效,Android下JUnit的配置参见另外一篇博客:Android--JUnit单元测试:

1 package com.example.sqlitedbdemo.db;
2
3 import java.util.List;
4 import java.util.Map;
5
6 import com.examle.sqlitedbdemo.dao.PersonDao;
7 import com.examle.sqlitedbdemo.service.PersonService;
8
9 import android.test.AndroidTestCase;
10 import android.util.Log;
11
12 public class TestDb extends AndroidTestCase {
13 private final String TAG = "main";
14
15 public TestDb() {
16 // TODO Auto-generated constructor stub
17 }
18
19 public void createDB() {
20 DbOpenHelper helper = new DbOpenHelper(getContext());
21 helper.getWritableDatabase();
22 }
23
24 public void insertDb() {
25 PersonService service = new PersonDao(getContext());
26 Object[] params1 = { "张龙", "beijing", "male" };
27 boolean flag = service.addPerson(params1);
28 Object[] params2 = { "赵虎", "shanghai", "male" };
29 flag = flag&&service.addPerson(params2);
30 Object[] params3 = { "王朝", "HK", "male" };
31 flag = flag&&service.addPerson(params3);
32 Object[] params4 = { "马汉", "beijing", "female" };
33 flag = flag&&service.addPerson(params4);
34 Log.i(TAG, "-----插入数据----->>" + flag);
35 }
36
37 public void deleteDb() {
38 PersonService service = new PersonDao(getContext());
39 Object[] params = { 1 };
40 boolean flag = service.deletePerson(params);
41 Log.i(TAG, "-----删除数据----->>" + flag);
42 }
43
44 public void updateDb() {
45 PersonService service=new PersonDao(getContext());
46 Object[] params = { "张三", "上海", "男","2" };
47 boolean flag=service.updatePerson(params);
48 Log.i(TAG, "---------->>" + flag);
49 }
50
51 public void getDb(){
52 PersonService service=new PersonDao(getContext());
53 Map<String, String> map = service.viewPerson(new String[]{"2"});
54 Log.i(TAG, "---------->>" + map.toString());
55 }
56
57 public void listDb() {
58 PersonService service = new PersonDao(getContext());
59 List<Map<String, String>> list = service.listPersonMaps(null);
60 Log.i(TAG, "---------->>" + list.toString());
61 }
62 }

insertDB()后,如果是在模拟器上调试,可以使用FIle Explorer工具导出mydb.db文件,使用SQLite Expert Professional(这是一个SQLite的管理软件,博客最后提供下载地址),打开数据库:

执行deleteDb()删除第一条数据:

执行updateDb()更新第二条数据:

执行getDb(),查询第二条数据,执行listDb(),查询全部数据,查看日志输出:

而如果是从事Android开发,还有必要了解另外一种操作SQLite的方式,使用SQLiteDatabase所提供的方法实现CRUD操作。主要有以下几个方法:
- long insert(String table ,String nullColumnHack,ContentValues values):插入一条数据。
- int delete(String table ,String whereCaluse,String[] whereArgs):根据条件,删除数据。
- int updata(String table,ContentValues values,String whereCaluse,String[] whereArgs):根据条件,更新数据
- Cursor query(...):根据条件,查询数据。提供多种重载方法,主要查询不同的条件。
下面以一个示例程序讲解一下使用SQLiteDatabase所提供的方法实现CRUD操作:
接口代码:
1 package com.examle.sqlitedbdemo.service;
2
3 import java.util.List;
4 import java.util.Map;
5
6 import android.content.ContentValues;
7
8 public interface PersonService2 {
9
10 public boolean addPerson(ContentValues values);
11
12 public boolean deletePerson(String whereClause, String[] whereArgs);
13
14 public boolean updatePerson(ContentValues values, String whereClause,
15 String[] whereArgs);
16
17 public Map<String, String> viewPerson(String selection,
18 String[] selectionArgs);
19
20 public List<Map<String, String>> listPersonMaps(String selection,
21 String[] selectionArgs);
22 }
实现代码:

1 package com.examle.sqlitedbdemo.dao;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import android.content.ContentValues;
9 import android.content.Context;
10 import android.database.Cursor;
11 import android.database.sqlite.SQLiteDatabase;
12
13 import com.examle.sqlitedbdemo.service.PersonService2;
14 import com.example.sqlitedbdemo.db.DbOpenHelper;
15
16 public class PersonDao2 implements PersonService2 {
17 private DbOpenHelper helper = null;
18
19 public PersonDao2(Context context) {
20 helper = new DbOpenHelper(context);
21 }
22
23 @Override
24 public boolean addPerson(ContentValues values) {
25 boolean flag = false;
26 SQLiteDatabase database = null;
27 long id = -1;
28 try {
29 database = helper.getWritableDatabase();
30 // 执行insert,返回当前行ID
31 id = database.insert("person", null, values);
32 flag = (id != -1 ? true : false);
33 } catch (Exception e) {
34 e.printStackTrace();
35 } finally {
36 if (database != null) {
37 database.close();
38 }
39 }
40 return flag;
41 }
42
43 @Override
44 public boolean deletePerson(String whereClause, String[] whereArgs) {
45 boolean flag = false;
46 SQLiteDatabase database = null;
47 int count = 0;
48 try {
49 database = helper.getWritableDatabase();
50 // 执行删除操作,返回影响行数
51 count = database.delete("person", whereClause, whereArgs);
52 flag = (count > 0 ? true : false);
53 } catch (Exception e) {
54 e.printStackTrace();
55 } finally {
56 if (database != null) {
57 database.close();
58 }
59 }
60 return flag;
61 }
62
63 @Override
64 public boolean updatePerson(ContentValues values, String whereClause,
65 String[] whereArgs) {
66 boolean flag = false;
67 SQLiteDatabase database = null;
68 int count = 0;
69 try {
70 database = helper.getWritableDatabase();
71 // 执行更新操作,返回影响行数
72 count = database.update("person", values, whereClause, whereArgs);
73 flag = (count > 0 ? true : false);
74 } catch (Exception e) {
75 e.printStackTrace();
76 } finally {
77 if (database != null) {
78 database.close();
79 }
80 }
81 return flag;
82 }
83
84 @Override
85 public Map<String, String> viewPerson(String selection,
86 String[] selectionArgs) {
87 SQLiteDatabase database = null;
88 Cursor cursor = null;
89 Map<String, String> map = new HashMap<String, String>();
90 try {
91 database = helper.getReadableDatabase();
92 // 设置查询条件
93 cursor = database.query(true, "person", null, selection,
94 selectionArgs, null, null, null, null);
95 int cols_len = cursor.getColumnCount();
96 while (cursor.moveToNext()) {
97 for (int i = 0; i < cols_len; i++) {
98 String cols_key = cursor.getColumnName(i);
99 String cols_value = cursor.getString(cursor
100 .getColumnIndex(cols_key));
101 if (cols_value == null) {
102 cols_value = "";
103 }
104 map.put(cols_key, cols_value);
105 }
106 }
107 } catch (Exception e) {
108 e.printStackTrace();
109 }
110 return map;
111 }
112
113 @Override
114 public List<Map<String, String>> listPersonMaps(String selection,
115 String[] selectionArgs) {
116 List<Map<String, String>> list = new ArrayList<Map<String, String>>();
117 SQLiteDatabase database = null;
118 Cursor cursor = null;
119 try {
120 database = helper.getReadableDatabase();
121 cursor = database.query(false, "person", null, selection,
122 selectionArgs, null, null, null, null);
123 int cols_len = cursor.getColumnCount();
124 while (cursor.moveToNext()) {
125 Map<String, String> map = new HashMap<String, String>();
126 for (int i = 0; i < cols_len; i++) {
127 String cols_key = cursor.getColumnName(i);
128 String cols_value = cursor.getString(cursor
129 .getColumnIndex(cols_key));
130 if (cols_value == null) {
131 cols_value = "";
132 }
133 map.put(cols_key, cols_value);
134 }
135 list.add(map);
136 }
137 } catch (Exception e) {
138 e.printStackTrace();
139 }
140 return list;
141 }
142
143 }

最后和上面一下,创建一个测试类来测试这个数据库操作:

1 package com.example.sqlitedbdemo.db;
2
3 import java.util.List;
4 import java.util.Map;
5
6 import com.examle.sqlitedbdemo.dao.PersonDao2;
7 import com.examle.sqlitedbdemo.service.PersonService2;
8
9 import android.content.ContentValues;
10 import android.test.AndroidTestCase;
11 import android.util.Log;
12
13 public class TestDb2 extends AndroidTestCase {
14 private final String TAG = "main";
15
16 public TestDb2() {
17 // TODO Auto-generated constructor stub
18 }
19
20 public void addPerson() {
21 PersonService2 service2 = new PersonDao2(getContext());
22 ContentValues values1 = new ContentValues();
23 values1.put("name", "张龙");
24 values1.put("address", "beijing");
25 values1.put("sex", "male");
26 boolean flag = service2.addPerson(values1);
27 ContentValues values2 = new ContentValues();
28 values2.put("name", "赵虎");
29 values2.put("address", "shanghai");
30 values2.put("sex", "male");
31 flag = flag&&service2.addPerson(values2);
32 ContentValues values3 = new ContentValues();
33 values3.put("name", "王朝");
34 values3.put("address", "HK");
35 values3.put("sex", "male");
36 flag = flag&&service2.addPerson(values3);
37 ContentValues values4 = new ContentValues();
38 values4.put("name", "王朝");
39 values4.put("address", "HK");
40 values4.put("sex", "male");
41 flag = flag&&service2.addPerson(values4);
42 Log.i(TAG, "----------->>" + flag);
43 }
44
45 public void deletePerson() {
46 PersonService2 service2 = new PersonDao2(getContext());
47 boolean flag = service2.deletePerson(" id =?", new String[]{"1"});
48 Log.i(TAG, "----------->>" + flag);
49 }
50
51 public void updatePerson(){
52 PersonService2 service2 = new PersonDao2(getContext());
53 ContentValues values = new ContentValues();
54 values.put("name", "张三");
55 values.put("address", "上海");
56 values.put("sex", "男");
57 boolean flag=service2.updatePerson(values, " id=? ", new String[]{"2"});
58 Log.i(TAG, "----------->>" + flag);
59 }
60
61 public void viewPerson(){
62 PersonService2 service2 = new PersonDao2(getContext());
63 Map<String, String> map=service2.viewPerson(" id=? ", new String[]{"2"});
64 Log.i(TAG, "----------->>" + map.toString());
65 }
66 public void listPerson(){
67 PersonService2 service2 = new PersonDao2(getContext());
68 List<Map<String, String>> list=service2.listPersonMaps(null,null);
69 Log.i(TAG, "----------->>" + list.toString());
70 }
71 }

实现的功能和上面一样,这里就不展示效果图了,但是因为是上面两种操作数据库的方式是在一个应用中完成的,并且数据一样,执行第二个测试类的时候,需要把之前创建的数据库删除,详情参见源码。
SQLite事务
SQLite的事务通过SQLiteDatabase中包含的两个方法对其进行控制:beginTransaction(),开始事务;endTransaction(),结束事务。除此之外,SQLiteDatabase还提供了一个inTransaction()方法用来判断当前上下文是否处于事务环境中。当程序执行endTransaction()方法时将会结束事务,到底是回滚事务还是提交事务取决于SQLiteDatabase是否调用了setTransactionSuccessful()方法来设置事务标志,如果程序事务执行中调用该方法设置了事务成功则提交事务,否则程序将回滚事务。
示例源码下载
总结
上面就基本讲解了SQLite在Android中的时候,虽然有两种操作方式,并且直接使用SQL语句操作数据库对于熟悉SQL语句的开发者开说,是非常贴心的,但是在Android中,还是有必要了解一下使用SQLiteDatabase提供的方法操作数据库的方式,因为Android有一个内容提供者(Content Provider),可以使用外部应用访问内部应用的数据,它传递数据的形式,大部分是与SQLiteDatabase内置方法的参数一致的,所以如果同一使用SQLiteDatabase提供的方法操作数据库,是很方便的,无需额外转换SQL语句。
本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/p/android_SQLite.html,如需转载请自行联系原作者