Android 数据存取之 Databases
在 Android 平台上可以操作数据库,这是第一次接触 Android 时的惊艳之一。在
Android 平台上,绑定了 SQLite 数据库,这个数据库系统也是极具性格的,它
的最大的应用场景是嵌入式系统,进一步了解可以参看这里。
如果有 JDBC 的经验,那么在这里会容易的多。Android 中操作数据库首先要通
过一个 类:android.database.sqlite.SQLiteOpenHelper。它封装了如何打开
一个数据库,其中当然也包含如果数据库不存在 就创建这样的逻辑。看一个例
子:
view plaincopy to clipboardprint?
1. pubilc class DatabaseHelper extends SQLiteOpenHelper {
2. private static final String DATABASE_NAME = "com.roiding.simple.note";
3. private static final int DATABASE_VERSION = 1;
4. private static final String NOTES_TABLE_NAME = "notes";
5.
6. DatabaseHelper(Context context) {
7. super(context, DATABASE_NAME, null, DATABASE_VERSION);
8. }
9.
10. @Override
11. public void onCreate(SQLiteDatabase db) {
12. db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME
13. + " (id integer primary key autoincrement, name text);");
14. }
15.
16. @Override
17. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersio
n) {
18. db.execSQL("DROP TABLE IF EXISTS notes");
19. onCreate(db);
20. }
21. }
这里面,如下的语句需要解释:
super(context, DATABASE_NAME, null, DATABASE_VERSION)
数据库连接的初始化,中间的那个 null,是一个 CursorFactory 参数,
没有仔细研究这个参数,暂时置空吧。
public void onCreate(SQLiteDatabase db)
这里面的 onCreate 是指数据库 onCreate 时,而不是 DatabaseHelper 的
onCreate。也就是说,如果已经指定 database 已经存在,那么在重新运
行程序的时候,就不会执行这个方法了。要不然,岂不是每次重新启动程
序都要重新创建一次数据库了!在这个方法中,完成了数据库的创建工作。
也就是那个 execSQL()方法。
public void onUpgrade(SQLiteDatabase db, int oldVersion, int
newVersion)
在程序的开发维护过程中,数据库的结构可能会有变化,那么这个方法就
有用处了。在 DatabaseHelper 这个对象一创建时,就已经把参数
DATABASE_VERSION 传入,这样,如果 Android 发现此版本与现有版本不
一致,就会调用这个 onUpgrate 方法。于是,可以在这里面实现一些数据
的 upgrade 工作,比如说创建一个临时表,将数据由临时表中转到新的表
结构中。需要注意的是,这里面的 onUpgrade 是在版本不一致时调用,也
就是说不管当前需要的版本高于现有版本还是低于现有版本,都会出发这
个方法,类似的这种情况,就需要对 oldVersion 和 newVersion 进行判
断之后再决定使用什么策略来更新数据。
在 Android 中,数据库存放在 /data/data/PACKAGE_NAME/databases 目录下。
接下来就可以使用这个 Helper 来操作数据库了,操作数据库也就无非是增、删、
改、查。先看一个增的例子:
view plaincopy to clipboardprint?
1. public static void insert(Context context, String s) {
2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
3.
4. String table = "notes";
5. String nullColumnHack = "id";
6.
7. ContentValues values = new ContentValues();
8. values.put("name", "www.roiding.com");
9.
10. long id = mOpenHelper.getReadableDatabase().insert(table,
11. nullColumnHack, values);
12.
13. mOpenHelper.getReadableDatabase().close();
14. }
DatabaseHelper mOpenHelper = new DatabaseHelper(context);
如果和 JDBC 例子的话,这一步貌似就像是获得了一个 Statement,用它
就可以操作数据库了。
ContentValues values = new ContentValues();
Android 在向数据库中插入数据的时候,要求数据存放到 ContentValues
中,这里面的 ContentValues 其实就是一个 Map,Key 值是字段名称,Value
值是字段的值。这样,也许你会发现一个问题,那数据类型怎么办?其实
在 SQLite 数据库中就是没有数据类型的, 一切都是字符串。
mOpenHelper.getReadableDatabase().insert(table,nullColumnHack,
values);
将数据入库,注意这里面有一个 nullColumnHack,看文档是和所有字段
都是空的记录有关系,我没有去实验他具体的效果,只是随便给他一个字
段名称。
再看一个查的例子:
view plaincopy to clipboardprint?
1. public static void select(Context context) {
2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
3.
4. String table = "notes";
5. String[] columns = new String[] { "id", "name" };
6. String selection = "id>? and name<>?";
7. String[] selectionArgs = new String[] { "0", "roiding.com" };
8. String groupBy = null;
9. String having = null;
10. String orderBy = "id desc";
11. String limit = "1";
12.
13. Cursor c = mOpenHelper.getReadableDatabase().query(table,
14. columns, selection, selectionArgs, groupBy, having, orderBy, limit);
15.
16. c.moveToFirst();
17. for (int i = 0; i < c.getCount(); i++) {
18. String s = c.getString(1);
19. c.moveToNext();
20. }
21. c.close();
22. mOpenHelper.getReadableDatabase().close();
23. }
DatabaseHelper mOpenHelper = new DatabaseHelper(context);
于前文中的相同
mOpenHelper.getReadableDatabase().query();
通过 mOpenHelper.getReadableDatabase(),会得到一个 SQLiteDatabase
类型的只读的数据库连接,在这里直接调用了他的 query 方法。这个 query
方法相对复杂,因为他将一个完整的 SQL 语句拆成了若干个部分:
o
o
table:表名。相当于 SQL 的 from 后面的部分。那如果是多表联合
查询怎么办?那就用逗号将两个表名分开,拼成一个字符串作为
table 的值。
columns:要查询出来的列名。相当于 SQL 的 select 后面的部分。
o
o
o
o
o
o
selection:查询条件,相当于 SQL 的 where 后面的部分,在这个
语句中允许使用“?”,也就是说这个用法和 JDBC 中的
PreparedStatement 的用法相似。
selectionArgs:对应于 selection 的值,selection 有几个问号,
这里就得用几个值。两者必须一致,否则就会有异常。
groupBy:相当于 SQL 的 group by 后面的部分
having:相当于 SQL 的 having 后面的部分
orderBy:相当于 SQL 的 order by 后面的部分,如果是倒序,或者
是联合排序,可以写成类似这样:String orderBy = “id desc,
name”;
limit:指定结果集的大小,它和 Mysql 的 limit 用法不太一样,
mysql 可以指定从多少行开始之后取多少条,例如“limit
100,10”,但是这里只支持一个数值。
c.moveToFirst();
这一句也比较重要,如果读取数据之前,没有这一句,会有异常。
c.getString(1);
与 JDBC 一致了,Android 不支持按字段名来取值,只能用序号。
再看一个删除和修改的例子:
view plaincopy to clipboardprint?
1. public static void delete(Context context) {
2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
3.
4. String table = "notes";
5. String selection = "id>? and name<>?";
6. String[] selectionArgs = new String[] { "0", "roiding.com" };
7. String whereClause = selection;
8. String[] whereArgs = selectionArgs;
9.
10. mOpenHelper.getWritableDatabase().delete(table, whereClause, whereArgs)
;
11. mOpenHelper.getWritableDatabase().close();
12. }
有了上面的基础这里就容易理解了,这里的 whereClause 相当于前面的
selection,whereArgs 相当于前面的 selectionArgs。
view plaincopy to clipboardprint?
1. public static void update(Context context) {
2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
3.
4. String table = "notes";
5. String selection = "id>? and name<>?";
6. String[] selectionArgs = new String[] { "0", "roiding.com" };
7. String whereClause = selection;
8. String[] whereArgs = selectionArgs;
9.
10. ContentValues values = new ContentValues();
11. values.put("name", "www.roiding.com");
12.
13. mOpenHelper.getWritableDatabase().update(table, values,
14. whereClause, whereArgs);
15. mOpenHelper.getWritableDatabase().close();
16. }
这个 update 的用法,综合 select 和 delete 就可以理解。
注意:
Cursor 和 Databases 要及时关闭,不然也会有异常。
getWritableDatabase()和 getReadableDatabase()在当前的 Android 版
本中貌似可以通用,像上面的 insert,用的就是 getReadableDatabase。
在真实的应用中,会对上面这些基本操作做更高一级的抽象和封装,使之更容易
使用。在 select 时,除了用上述的方法,将分段的 SQL 语句传进去之外,Android
还支持一种方法:使用 SQLiteQueryBuilder。如果使用的是上述的分段 SQL 语
句的方法,在 Android 的内部实现中,也是先将分段的 SQL 使用
SQLiteQueryBuilder 的静态方法来生成一个真正的 SQL 的,而且,我没有看出
来使用 SQLiteQueryBuilder 的优势。
Tags: delete, getReadableDatabase, getWritableDatabase, insert, query,
SQLite, SQLiteOpenHelper, update
每个应用程序都要使用数据,Android 应用程序也不例外,Android 使用开源的、与
操作系统无关的 SQL 数据库--SQLite,本文介绍的就是如何为你的 Android 应用程序创建
和操作 SQLite 数据库。
数据库支持每个应用程序无论大小的生命线,除非你的应用程序只处理简单的数
据,那么就需要一个数据库系统存储你的结构化数据,Android 使用 SQLite 数据库,它是
一个开源的、支持多操作系统的 SQL 数据库,在许多领域广泛使用,如 Mozilla FireFox 就
是使用 SQLite 来存储配置数据的,iPhone 也是使用 SQLite 来存储数据的。
在 Android 中,你为某个应用程序创建的数据库,只有它可以访问,其它应用程
序是不能访问的,数据库位于 Android 设备/data/data/ /databases 文件夹中,在这篇文章
中,你将会学习到如何在 Android 中创建和使用数据库。
SQLite 数据库
使用 Eclipse 创建一个 Android 项目,取名为 Database,如图 1 所示:
图 1 数据库-使用 Eclipse 创建你的 Android 新项目
创建 DBAdapter 辅助类
操作数据库的最佳实践是创建一个辅助类,由它封装所有对数据库的复杂访问,
对于调用代码而言它是透明的,因此我创建了一个 DBAdapter 的辅助类,由它创建、打开、
关闭和使用 SQLite 数据库。
首先,在 src/ 文件夹(在这个例子中是 src/net.learn2develop.Database)下添加一
个 DBAdapter.java 文件。
在 DBAdapter.java 文件中,导入所有你要使用到的命名空间:
package net.learn2develop.Databases;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBAdapter
{
}
接下来创建一个数据库,取名为 bookstitles,字段如图 2 所示。
图 2 数据库字段
在 DBAdapter.java 文件中,定义清单 1 中的常量。
清单 1 定义 DBAdapter.java 文件中的常量
package net.learn2develop.Database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBAdapter
{
public static final String KEY_ROWID = "_id";
public static final String KEY_ISBN = "isbn";
public static final String KEY_TITLE = "title";
public static final String KEY_PUBLISHER = "publisher";
private static final String TAG = "DBAdapter";
private static final String DATABASE_NAME = "books";
private static final String DATABASE_TABLE = "titles";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_CREATE =