在SQL数据库中保存数据Saving Data in SQL Databases
在数据库里面存储数据是数据复用和结构化数据的理想方案。这里默认你已经有一些数据库的基本知识了。重点在教你使用Andorid中的SQLite数据库。这里面的api在android.database.sqlite包中。
定义模式和约定
SQL数据库中的一个重要原则就是模式:关于数据库是如何组织的一个正式的定义。模式反映在SQL中你用来创建数据库的声明中。你可能会发现使用一个使用一个伴随的类十分有帮助,这个类叫做约定类,它以一个系统的,和自我文件的方式显示地说明了模式中的结构。
约定类是一个包含名字、URI等常量、表和栏目的容器。contract类允许你在同一个包中的其他的类中使用相同的变量。这使得你可以在修改了一个栏的名字的时候,你的代码的其他地方也会改变。
组织contract类的一个好办法就是把整个数据库的全局定义放在类的根层次下。然后给每一个列举栏目的表创建内部类。
*********提示*************
通过实现BaseColums接口,你的内部类可以继承一个自由的主键字段,叫做_ID,它会被一些例如cursor adaptors等的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"; ... } }
使用SQLHelper类创建一个数据库
一旦你定义好了你的数据库的样子,你就可以实现创建和维护数据库和表的方法了。下面是一些典型的创建和删除表的语句。
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()。
********提示*********
因为是长时间的运行,所以要确保你的调用是在后台线程当中。可以使用AnsyTask或者IntentService
**********************
使用SQLiteOpenHelper,请继承父类并重写onCreat,onUpgrade,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());
向数据库中添加信息
通过insert()方法传入ContentValue对象来向数据库总插入信息:
// 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);
第一个参数是表名,第二个是指明一栏当ContentValue中的该项为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中的一行,你需要使用cursor中的移动的方法,而且你每一次读数据都需要调用这些方法。通常你要首先调用moveToFirst()方法,它会把读的位置放置于结果的第一个条目上。对于每一行,你可以通过调用Cursor中的方法来获取一栏的数据,比如说getString()或者getLong()。对于每一个get方法,你都必须传入你需求的栏目的 索引。你可以通过调用getColumnIndex()或者getColumnIndexOrThrow()来获取。例如:
cursor.moveToFirst(); long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID) );//如果没有这栏就抛异常。
从数据库中删除数据
要删除数据库表中的一行数据,你需要提供用来确定这行的标准。数据库API提供了一种可以创建选择标准并防范SQL注入攻击的机制。这个机制将选择的说明分分割为选择的从句和选择的参数。从句定义了需要查找的栏目,同时可以组合栏目测试。参数就是从句中需要测试的值。因为对于结果的处理不同于一般的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()的内容数值的语法,和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);
(译者:说实话,官方开发文档保留了很多思想性的东西,包括东西构建的原理和联系,比如说这里面对于增删改查的语法的解析,我虽然也读过数据库的书但是当时并没有意识到该就是使用插入语法指定栏位置,然后用删除的语法来指定行。推荐大家多多阅读官方文档,体会设计者的思想,而不仅仅是只会用就行了,这样才能有长远的发展。)