为了重复或结构化的数据而保存数据到数据库是非常理想,比如联系信息。一般来说在Android上假设你熟悉SQL数据库,那么它将帮助你开始使用SQLite数据库。API在Android上通过调用android.database.sqlite包而需要使用一个数据库是很有必要的。
1、定义一个模式和合同
SQL数据库的一个主要原则的模式:一个正式宣布数据库如何被组织。在SQL语句中,模式是反映你可以用它来创建你的数据库。你可能会发现,这有助于创建一个同伴类,称为合同类,你的模式在一个系统和自我记录的方式下显式地指定模式的布局。
对于常量来说合同类是一个容器,它们还为uri、表和列定义名称。该合同类允许你使用相同的常量跨所有其他类放在同一个包内。这可以让你在一个地方改变一个列名称,并让它通过你的代码进行传播。
组织一个合同类的较好的方式是设置定义,在根层次的类中它对于你的整个数据库来讲是全局。然后为每个表创建一个内部类并列举了其列。
例如,这个代码片段为单个表定义了表名和列名:
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";
...
}
以防止有人不小心实例化类的合同,给它一个空构造函数。
// 防止FeedReaderContract类被实例化
private FeedReaderContract()
{}
2、使用SQL助手创建一个数据库
无论你的数据库看起来如何一旦你已经定义了,你应该实现方法,创建和维护数据库和表。下面是一些典型的语句,创建和删除一个表:
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商店数据库在私人磁盘空间相关联的应用程序。你的数据是安全的,因为在默认情况下这个区域是不向其他应用程序公开。
在SQLiteOpenHelper类中一套api是很有用的。当你用这个类为你的数据库来获取引用时,系统只在需要时执行可能长时间运行的操作创建和更新数据库,而不是在应用程序启动。所有你需要做的就是调用getWritableDatabase()或getReadableDatabase()。
注意:因为他们可以长时间运行,确保你调用getWritableDatabase()或getReadableDatabase()在一个后台线程,例如IntentService或 AsyncTask。
使用SQLiteOpenHelper,创建一个子类,覆盖了onCreate()、onUpgrade()和onOpen()回调方法。你可能也想实现onDowngrade(),但这不是必需的。
举例来说,这里的一个实现,它使用一些的SQLiteOpenHelper上面显示的命令:
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());
3、 将信息存入数据库
通过一个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()仅仅是表名。第二个参数提供了一个列的名称,如果ContentValues是空的那么该框架可以插入NULL(如果你相反将这个参数设置为“NULL”,那么这个框架不会插入一行当没有值)。
4、 从数据库中读取信息
通过使用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()方法开始,它将“read position”作为第一个入口进入结果。对于每一行, 你都可以调用Cursor
的其中一个获取方法来读取列的值,如getString()或getLong()。对于每个get方法,你必须通过你所期望的索引位置的列,你可以通过调用getColumnIndex()或getColumnIndexOrThrow()来获取。例如:
cursor.moveToFirst();
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
);
5、 从数据库中删除信息
从一个表中删除行时,你需要提供选择标准,确定行。数据库API提供了一种机制来创建选择标准,防止SQL注入。将选择规范机制划分成选择条款和选择参数。从这个子句定义的列看,它依然允许你合并列测试。这个参数作为再一次测试的值绑定到条款。因为结果的方式处理不同与一个常规的SQL语句,它是免疫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, mySelection, selectionArgs);
6、 更新数据库
当你需要修改你的数据库值的一个子集时,请使用update()方法。
更新表结合了内容值语法的insert()和delete()语法的地方。
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);