把数据保存在数据库里里面,对于那些重复性的以及需要组织的数据,是一种理想的办法,比方说联系人信息。这一节假定对sQL操作有一定的了解,来讲解一下在android里面SQL的操作。android平台上操作SQL需要使用的API在android.database.sqlite这个包里面。、
定义架构和约定
在SQL里面一个最主要的规则就是架构:正式的指出数据库如何组织。架构反应在你当前使用的用来创建数据库的SQL语句里面。你会发现这个对于你同时创建多个类很有帮助,如:一个约定类信息,它明确的指出你的架构在系统上的设计和自我描述的方法。
一个约定类是用来存放URI,table,column的名字的类。这个类允许你在同一个包的其它的类里面使用它里面的常量。这么做如果你在一个地方修改了column的名字,那么这个改变贯穿你的代码,不需要一个地方一个地方去修改。
组织一个约定类的好办法就是把那些要在数据库里面使用的公共常量定义都放在类的最上面。然后为每一个table创建一个内部类,可以枚举这个table里面的columns.
注意:通过实现
BaseColumns接口,你的内部类可以继承一个类似为
_ID的私有key域,android的一些类比方说cursor adaptor 就希望用到它。这个不是必须的,但是这个可以让你的数据库在android framework层上更好的运行。
比方说,下面这一段就是在FeedReaderContract类里面创建了一个为table定义table名字和column名字的内部类:
public static abstract class FeedEntry implements BaseColumns {
public static final String TABLE_NAME = "entry";
public static final String COLUMN_NAME_ENTRY_ID = "entryid";
public static final String COLUMN_NAME_TITLE = "title";
public static final String COLUMN_NAME_SUBTITLE = "subtitle";
...
}
为了防止有人意外的去实例化这个类,给它提供一个空的构造函数
// Prevents the FeedReaderContract class from being instantiated.
private FeedReaderContract() {}
使用
SQL Helper创建一个数据库
一旦定义了数据库里面要存储的信息,下面就要实现方法来创建和维护这个数据库和里面的table。下面是用来创建和删除table的标准语句:
private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +
FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
... // Any other options for the CREATE command
" )";
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + TABLE_NAME_ENTRIES;
就像之前我们说的保存数据到内部存储器,android系统也会把数据库文件保存在APP对应的内部存储器上。这写数据时安全的,因为默认的这个区域是不允许其他的程序访问的。
用得到的设置方法都在SQLiteOpenHelper这个类里面。当用这个类里面的方法操作数据库里面的内容的时候,系统仅仅在你需要的时候,当应用不是处在启动的时候,去执行那些潜在的耗时操作,如更新, 创建数据库。你需要做的仅仅是调用
getWritableDatabase()
和 getReadableDatabase()
.
注意:因为数据库的操作可能是耗时的操作,所以保证调用
getWritableDatabase()
或者 getReadableDatabase()的时候,是在一个后台线程里面调用,例如
AsyncTask
或者IntentService
.
使用SQLiteOpenHelper,要去创建一个它的子类,重写
onCreate()
, onUpgrade()
and onOpen()这个三个回调函数。也可以重写
onDowngrade(),但这个不是不是必须的。
下面就是一个例子,使用了上面的一些命令语句。
public class FeedReaderDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "FeedReader.db";
public FeedReaderDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL(SQL_DELETE_ENTRIES);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
}
要操作数据库,实例化你创建的SQLiteOpenHelper的子类。
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
往数据库存储信息
插入数据到数据库的方法是,传递一个ContentValues对象给
insert()方法。
// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content);
// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
FeedReaderContract.FeedEntry.TABLE_NAME,
FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE,
values);
insert函数里面的第一个参数是table的名字,第二个参数指出那些如果没有定义值可以插入NULL的列,如果第三个参数为空,即没有valuues,那么这个执行的结果不会插入任何一条记录。
从数据库读取信息
使用query()方法读取数据库信息,把你的选择条件和想要读取的每条记录里面的哪些列提供给这个函数。这方法会得到
insert()
和 update()函数里面的元素,除非你想得到列表里面的信息,没有被插入数据库。结果会返回一个Cursor对象。
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
FeedReaderContract.FeedEntry._ID,
FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE,
FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED,
...
};
// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + " DESC";
Cursor c = db.query(
FeedReaderContract.FeedEntry.TABLE_NAME, // The table to query
projection, // The columns to return
selection, // The columns for the WHERE clause
selectionArgs, // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
sortOrder // The sort order
);
想要读取一行,使用
Cursor里面的方法移动一个位置,每次读取之前都要调用里面的方法移动一个位置。一般来说,首先要调用
moveToFirst()
,这个会把当前的读取位置放到得到的第一条记录的位置,对于每一列的值,可以调用getString()
or getLong()这种get方法来获得,必需传递你要读取的column的位置,这个位置可以使用
getColumnIndex()
or getColumnIndexOrThrow()得到。
cursor.moveToFirst();
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
);
从数据库删除信息
要从数据库删除一条记录,需要提供选择删除的规则,用来指出要删除的是哪条记录。数据库的API提供了一套创建选择规则的原理,以防止数据库注入。这个原理是把你的选择规则分别划分为选择语句和参数。选择语句定义了你要查找的columns,运行将几个列组合来作为检测条件,参数提供了选择语句里面的需要的判断值。因为这样做的结果是它的处理方式与常规的SQL处理方法不一样,从而防止了数据库的注入。
// Define 'where' part of query.
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selelectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);
更新数据库
当需要修改数据库一条记录里面的某个数值的时候,使用update()方法。
更新数据的方法里面组合了insert()里面的values语法部分和
delete()
里面的where语法部分。
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// New value for one column
ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based on the ID
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selelectionArgs = { String.valueOf(rowId) };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);