—— 不为跑赢世界,只为超越自己。
Android提供了四种不同的方式用于本地存储数据,上一篇我们讲了文件存储和SharedPreferences存储,现在我们来看 SQLite 存储。SQLite 是一个结构查询基础数据库,我们可以说它是一个关系数据库。Android 系统有内部已经实现了CRUD(创建、读取、更新、删除)操作,在 Android 的 android.database 和 android.database.sqlite 包中提供了一组可用的类。
在使用 SQLite 时,可能有两种不同的方式来执行不同的操作如创建、读取、更新和删除。一种是编写原始SQL语句,另一种是使用参数化函数,或者我们可以说是参数化查询。
创建数据库
使用 SQLiteOpenHelper 类在 Android 中创建数据库非常简单。SQLiteOpenHelper 是一个抽象类,具有两个抽象方法 onCreate(SQLiteDatabase db) 和 onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 以及更多对数据库有用的函数。每当我们需要创建数据库时,我们必须扩展 SQLiteOpenHelper 类,如下所示:
| 12345678910111213141516171819202122 | public class SqliteManager ``extends SQLiteOpenHelper {`` ``public static final String DATABASE_NAME = ``"test.db"``;`` ``public static final int version = ``1``;`` `` ``public SqliteManager(Context context) {`` ``super``(context, DATABASE_NAME, ``null``, version);`` ``}`` ``@Override`` ``public void onCreate(SQLiteDatabase sqLiteDatabase) {`` ``String dbQuery = ``"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT, description TEXT)"``;`` ``sqLiteDatabase.execSQL(dbQuery);`` ``}`` ``@Override`` ``public void onUpgrade(SQLiteDatabase sqLiteDatabase, ``int oldVersion, ``int newVersion) {`` `` ``}`` ``} |
|---|
onCreate(SQLiteDatabase sqLiteDatabase)方法在整个应用程序生命周期中只调用一次,每当第一次调用 SQLiteOpenHelper 类中的 getReadableDatabase() 或 getWritableDatabase() 函数时都会调用它,因此 SQLiteOpenHelper 类在创建后调用 onCreate() 方法数据库并实例化 SQLiteDatabase 对象。数据库名称在构造函数调用中传递。
onUpgrade(SQLiteDatabase db,int oldVersion, int newVersion)仅在升级APP版本并且数据库有更新时调用,因此需要更新版本时我们必须增加version的值,比如app从1.0.0升级到1.0.1的时候数据库需要升级的话,对应的version就要改成大于1的任何整数即可。在 onUpgrade 方法中,我们可以编写SQL语句来执行所需的任何操作。如增加一张新表,增加一条数据记录等等。
在 Sqlite 中插入、读取、删除和更新操作
执行插入、读取、删除、更新操作有两种不同的方式:
-
编写参数化查询(推荐)
-
编写原始SQL语句
参数化查询:这些查询使用SDK内置的函数来插入、读取、删除或更新数据。这些操作相关的函数在 SQLiteDatabase 类中提供。
原始SQL语句:这些是类似于 MySql、Sql Server 等其他数据库的 sql 查询语句,将这些SQL语句当做参数传给 rawQuery(String sql,String [] selectionArgs) 或 execSQL(String sql,Object [] bindArgs) 方法来执行操作。
注: Android 官方不建议使用原始SQL语句来执行插入、读取、更新、删除操作,建议始终使用 SQLiteDatabase 类的内置函数执行插入、查询、更新、删除操作。原因如下:
当使用原始SQL语句执行插入操作的时:
| 1234567 | public void insertItem(Item item) {`` ``String query = ``"INSERT INTO " + ItemTable.NAME + ``" VALUES (0,?,?)"``;`` ``SQLiteDatabase db = getWritableDatabase();`` ``db.execSQL(query, ``new String[]{item.name, item.description});`` ``db.close();``} |
|---|
在使用原始查询时,我们永远不会知道操作的结果,即db.execSQL的返回值是void类型。而使用参数化查询函数则会返回一个值来表示操作成功或失败。所以建议使用参数化查询。
插入操作
使用参数化查询执行插入操作,我们必须调用 SQLiteDatabase 类中内置的插入函数:
| 12 | public long insert(String tableName,String nullColumnHack,ContentValues values) |
|---|
insert()函数具有三个参数,tableName 是要插入数据表的名称。nullColumnHack可能为空,SQL不允许在插入一个完全空的行时不指定至少一个列名。如果提供的值为空,则不知道列名,并且无法插入空行。如果未设置为null,则nullColumnHack参数提供可为null的列名的名称,以便在值为空的情况下显式插入null。values是包含一行数据的键值对。键应该是列名值是列值。如果插入成功,insert 函数将返回一个 long 值,即插入的行数,否则返回 – 1。
举个栗子:
| 123456789 | public void addItem(Item item) {`` ``SQLiteDatabase db = getWritableDatabase();`` ``ContentValues contentValues = ``new ContentValues();`` ``contentValues.put(``"name"``, item.name);`` ``contentValues.put(``"description"``, item.description);`` ``db.insert(``"Items"``, ``null``, contentValues);`` ``db.close();``} |
|---|
更新操作
更新操作与插入操作非常相似,但它需要两个额外的参数,它不需要 nullColumnHack。它共有四个参数,其中两个类似于插入函数,即 tableName 和 contentValues。另外两个是 whereClause(String) 和 whereArgs(String[]):
| 12 | public int update(String tableName,ContentValues contentValues,String whereClause,String[] whereArgs) |
|---|
这里 whereClause 是告诉数据库在哪里更新表中的数据,建议在 whereClause 字符串中使用占位符 ? 表示将要传递的值。类似地,whereArgs 数组将包含那些传递给对应的占位符 ? 的值。如果成功,更新函数将返回受影响的行数,否则返回 0。
举个栗子:
| 1234567891011 | public void updateItem(Item item) {`` ``SQLiteDatabase db = getWritableDatabase();`` ``ContentValues contentValues = ``new ContentValues();`` ``contentValues.put(``"id"``, item.id);`` ``contentValues.put(``"name"``, item.name);`` ``contentValues.put(``"description"``, item.description);`` ``String whereClause = ``"id=?"``;`` ``String whereArgs[] = {item.id.toString()};`` ``db.update(``"Items"``, contentValues, whereClause, whereArgs);``} |
|---|
删除操作
与插入和更新类似,SQLiteDatabase 类中提供了删除功能,因此删除与更新功能非常相似,除了 ContentValues 对象,因为它在删除中不需要。delete 函数有三个参数,它们与更新函数的参数完全相似,使用方式与更新函数相同。
举个栗子:
| 1234567 | public void deleteItem(Item item) {`` ``SQLiteDatabase db = getWritableDatabase();`` ``String whereClause = ``"id=?"``;`` ``String whereArgs[] = {item.id.toString()};`` ``db.delete(``"Items"``, whereClause, whereArgs);``} |
|---|
这里 whereClause 是可选的,传递 null 将删除表中的所有行。如果 whereClause 传递,删除函数将返回受影响的行数,否则将返回 0。
注:如果要删除所有行并要求返回删除行的计数,则将 1 作为 whereClause 传递。
查询操作
从数据库表中读取与插入、更新和删除等其他函数有点不同。SQLiteDatabase 类提供query()方法来从表中读取数据。query() 方法被不同的参数集重载。它返回Cursor对象,因此 Cursor 是一个带有查询数据的结果集,它提供了不同的功能,在读取数据时非常有用。
以下是一些重载的查询函数:
public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
public Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
查询重载函数中的大多数参数都是可选的,除了 table 和不同的任何其他参数都可以作为 null 传递。如果 distinct 作为 true 传递,则游标数据集将没有任何重复行。
| 1234567891011121314151617181920212223242526272829 | ``public ArrayList<item> readAllItems() {`` ``ArrayList<item> items = ``new ArrayList<>();`` ``SQLiteDatabase db = getReadableDatabase();`` ``//see above point 2 function`` ``Cursor cursor = db.query(``"Items"`` ``, ``null``// columns - null will give all`` ``, ``null``// selection`` ``, ``null``// selection arguments`` ``, ``null``// groupBy`` ``, ``null``// having`` ``, ``null``// no need or order by for now;`` ``if (cursor != ``null``) {`` ``while (cursor.moveToNext()) {`` ``// move the cursor to next row if there is any to read it's data`` ``Item item = readItem(cursor);`` ``items.add(item);`` ``}`` ``}`` ``return items;`` ``}`` ``private Item readItem(Cursor cursor) {`` ``Item item = ``new Item();`` ``item.id = cursor.getInt(cursor.getColumnIndex(ItemTable.COL_ID));`` ``item.name = cursor.getString(cursor.getColumnIndex(ItemTable.COL_NAME));`` ``item.description = cursor.getString(cursor.getColumnIndex(ItemTable.COL_DESCRIPTION));`` ``return item;`` ``}``</item></item> |
|---|