SQL数据库的主要原则是模式(schema): 一种数据库被如何组织的正式声明. 模式被反映在你用来创建你的数据库的SQL语句中. 你可能会发现创建一个同伴类很有用,它被称作(contract)协议类, 它用一种系统和自文档化的方式明确指定了你的模式的布局.
协议类是定义的URI、表格和列名称常量的容器. 协议类让你可以在同一个包的所有其它类那里使用相同的常量. 这让你可以在一个地方对列名称的改变传播到你所有的代码.
组织一个协议类最好的方式是将对你的整个数据库全局可用的定义放置到类的根一级别 . 然后为每一个表创建一个内部类,并枚举出它们的列.
注意: 通过实现 BaseColumns 接口, 你的内部类可以继承到一个称作 _ID 的主键域,一些诸如游标适配器的Android类将希望有这个东西 . 它也不是一定要有的,但它有助于你的数据库同Android框架更加的协调 .
例如,这个小代码块为一个表定义了表名和列名 :
public final class FeedReaderContract { // To prevent someone from accidentally instantiating the contract class, // give it an empty constructor. public FeedReaderContract() {} /* Inner class that defines the table contents */ 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"; ... } }
一旦你定义好了数据库是什么样子,你就会要实现创建和维护数据库及其数据表的方法 . 下面是一些创建和删除一个表格的典型语句 :
private static final String TEXT_TYPE = " TEXT"; private static final String COMMA_SEP = ","; private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + FeedEntry._ID + " INTEGER PRIMARY KEY," + FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + 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 " + FeedEntry.TABLE_NAME;
就像是你保存在设备的 内部存储 中的文件, Android 将你的数据库保存在同应用程序相关联的私有磁盘空间上. 你的数据时受到保护的,因为这一区域默认不能被其它应用程序所访问 .
SQLiteOpenHelper 类中有一堆实用的API . 当你想要使用这个类来获取你的数据库的引用时,系统只会在需要时并且不会是应用启动时,执行创建和更新数据库的可耗时较长的操作. 你总共需要做的就是调用 getWritableDatabase() 和 getReadableDatabase().
注意: 以为它们可以是耗时较长的,请确保你是在一个后台线程中调用的 getWritableDatabase() 和 getReadableDatabase(), 使用诸如 AsyncTask 或者 IntentService.
为了使用 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());
通过将一个 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(FeedEntry.COLUMN_NAME_ENTRY_ID, id); values.put(FeedEntry.COLUMN_NAME_TITLE, title); values.put(FeedEntry.COLUMN_NAME_CONTENT, content); // Insert the new row, returning the primary key value of the new row long newRowId; newRowId = db.insert( FeedEntry.TABLE_NAME, FeedEntry.COLUMN_NAME_NULLABLE, values);
insert() 的第一个参数就是数据表的名称. 第二个参数提供在 ContentValues 为空的事件中,框架可以向其中插入NULL的一列的名称 (如果你将此设置为 "null", 那么在没有值的时候框架将不会插入一行 ).
要从数据库读取数据,使用 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 = { FeedEntry._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_UPDATED, ... }; // How you want the results sorted in the resulting Cursor String sortOrder = FeedEntry.COLUMN_NAME_UPDATED + " DESC"; Cursor c = db.query( 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(), 它会将“读取位置”放在结果的第一条的位置 . 对于每一行,你可以通过调用 Cursor 中的摸一个获取方法来读取一列的值 , 比如 getString() 或者 getLong(). 对于每一个获取方法,你必须传入你想要获取的列的索引位置 , 它可以通过调用 getColumnIndex() 或者 getColumnIndexOrThrow() 获取到. 例如:
cursor.moveToFirst(); long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID) );
要从数据库中删除行,你需要提供识别这些行的选择条件. 数据库API提供了创建能够防止数据库注入的选择条件的机制. 该机制将选择指标分成选择语句和选择参数 . 语句部分定义了要查找的列,同时也允许你结合列测试 . 参数部分是绑定到语句中的测试用的值 . 因为结果不是处理同一个常规的SQL语句,它也就不会受到SQL注入的侵害.
// Define 'where' part of query. String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; // Specify arguments in placeholder order. String[] selectionArgs = { 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(FeedEntry.COLUMN_NAME_TITLE, title); // Which row to update, based on the ID String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; String[] selectionArgs = { String.valueOf(rowId) }; int count = db.update( FeedReaderDbHelper.FeedEntry.TABLE_NAME, values, selection, selectionArgs);
来源:
http://developer.android.com/training/basics/data-storage/databases.html