1. 數據類型
與其它數據庫不同,SQLite的數據類型很簡單,只有NULL(空類型)、INTEGER(整型)、REAL(浮點型)、TEXT(字符串型)、BLOB(二進制型)。
SQLite為動態數據類型(弱引用),當向數據庫中插入某個值時,會檢查該值的類型,若類型與插入列不匹配,SQLite會嘗試將該值轉換成相應類型。
2. 創建數據庫
如果對SQL的操作不了解,建議先看看這一塊:MySQL入門筆記(一)
Android中提供了一個SQLiteOpenHelper類,使用該類可非常方便地進行數據庫的創建與升級。
SQLiteOpenHelper是一個抽象類,必須實現onCreate()、onUpgrade(),這兩個方法分別用於數據庫的創建與升級,onCreate()在數據庫不存在的情況下會調用,而onUpgrade()則是當版本號高於已有數據庫時調用。
SQLiteOpenHelper構造函數的第一個參數是context,第二個參數是數據庫的名稱,第三個參數允許在查詢數據時返回一個自定義的Cursor,直接傳入null即可,第四個參數是數據庫版本號,用於數據庫的升級操作。
調用getReadableDtabase()或者getWritableDatabase()方法即可創建或打開數據庫,同時返回一個SQLiteDatabase對象,用於操作數據庫。這里,SQLiteDatabase對象有兩個重要方法,exexSQL方法和rawQuery方法,前者用於執行SQL語句,后者用於查詢記錄,后面會具體介紹。
拓展:Context與SQLiteDatabase的方法openOrCreateDatabase的區別 (context、SQLiteOpenHelper)
范例:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new SQLiteOpenHelper(this, "Library.db", null, 1) {
static final String CREATE_BOOK = "create table BOOK ("
+ "id integer primary key autoincrement, "
+ "name text, "
+ "author text)";
@Override
public void onCreate(SQLiteDatabase db) {
// 創建數據表
db.execSQL(CREATE_BOOK);
Toast.makeText(MainActivity.this, "創建成功!", Toast.LENGTH_SHORT).show();
}
// 用於升級數據庫
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}.getWritableDatabase();
}
}
3. 升級數據庫
在進行app的迭代時,往往也會伴隨着進行數據庫的升級,這就需要通過onUpgrade()方法實現。需要升級數據庫時,在實例化SQLiteOpenHelper時傳入一個更高的版本號即可。
范例:
假定進行兩次迭代:1. 新增一張表catelogy,2. 給表book增加category_id字段。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new SQLiteOpenHelper(this, "Library.db", null, 1) {
static final String CREATE_BOOK = "create table BOOK ("
+ "id integer primary key autoincrement, "
+ "name text, "
+ "author text,"
+ "category_id integer)";//第二次升級時增加
static final String CREATE_CATEGORY = "create table CATEGORY ("
+ "id integer primary key autoincrement, "
+ "category_name text,"
+ "category_code integer)";
static final String CHANGE_COLUMN = "alter table BOOK column category_id integer";
@Override
public void onCreate(SQLiteDatabase db) {
// 如果用戶當前是初次安裝本程序,即數據庫未被創建,則直接創建兩個表
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(MainActivity.this, "創建成功!", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
// 不使用break
case 1:
db.execSQL(CREATE_CATEGORY);
case 2:
db.execSQL(CHANGE_COLUMN);
default:
}
}
}.getWritableDatabase();
}
}
這里注意一個細節,onUpgrade()方法中的switch語句里的並未使用break,這是為了保證在跨版本升級時能順利依次完成中間的所有升級。
4. 數據操作
4.1 execSQL()和rawQuery()
上面提到,調用getReadableDatabase()或者getWritableDatabase()方法時會返回一個SQLiteDatabase對象,要對數據進行操作,就是使用這一對象。
結合上面的例子,我們可以看到SQLiteDatabase提供了一個調用SQL語句的方法execSQL(),通過這個方法可以調用除查詢語句以外的SQL語句,同時SQLiteDatabase類提供了一個rawQuery()方法,用於查詢數據。
(1)execSQL()
execSQL(String sql)
execSQL(String sql, Object[] bingArgs)
使用execSQL()方法傳入參數有兩種方式,第一種直接傳入SQL語句,如有參數則直接拼接在一起,例如:
String delete_sql = "delete from test where _id = " + idString;
db.execSQL(sql);
第二種方法則是在參數的位置插入占位符?,而后將具體參數傳給execSQL()方法的第二個參數,如有多個參數,則依次放入:
String delete_sql = "delete from test where _id = ?, name = ?";
db.execSQL(sql, new String[] {idString, nameString});
(2)rawQuery()
rawQuery(String sql, String[] selectionArgs)
rawQuery()方法的使用與execSQL()的第二種用法基本一致。
4.2 安卓提供的API
除了execSQL()方法和rawQuery()方法,安卓還針對數據庫的CRUD操作封裝了API,更加便於操作。
(1)insert()
insert(String table, String nullColumnHack, ContentValues values)
table:表名。
nullColumnHack:若某些允許為空的列沒有賦值,則自動賦值為null,傳入null即可。
values:ContentValues對象,相當於一個容器的作用,可通過它的put(key, values)方法(key為列名稱,values為該列的值)將每一行的數據一一添加,而后再傳入數據庫。如需插入多條記錄,插入下一條記錄前要調用clear()方法進行清空。
返回一個id值,即插入記錄的id。
范例:
//獲取SQLiteDatabase對象
db = mHelper.getWritableDatabase();
//實例化ContentValues對象並進行數據組裝
ContentValues values = new ContentValues();
values.put("name", "A");
values.put("author", "aa");
//插入數據
db.insert("BOOK", null, values);
//若需要插入多條記錄,在這一步一定要調用values的clear()方法清空現有的數據
values.clear();
values.put("name", "B");
values.put("author", "bb");
db.insert("BOOK", null, values);
(2)delete()
delete(String table, String whereClause, String[] whereArgs)
whereClause和whereArgs:指定刪除條件,若不指定則刪除表中所有數據。即SQL中where字句部分,占位符的使用同execSQL()與rawQuery(),例如將age > 30的記錄刪除,則第二個參數為"age > ?",第三個參數為new String[] {"30"}。
范例:
//刪除數據,?是一個占位符,第二、第三個參數合起來即刪除id > 2的記錄
db.delete("BOOK", "id > ?", new String[] {"2"});
(3)update()
update(String table, ContentValues values, String whereClause, String[] whereArgs)
values同Insert()方法,將更新后的數據通過ContentValues對象組裝,而whereClause和whereArgs則同delete()方法,指定更新哪些記錄。
范例:
ContentValues values = new ContentValues();
values.put("name", "D");
db.update("BOOK", values, "id < ?", new String[] {"3"});
(4)query()
query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
如果學過SQL語言的話,相信對上面這一長串的參數是比較容易理解的,基本上可以與SQL中select語句的結構對應上。調用query()方法會返回一個Cursor對象,里面含有所有查詢結果。
table:對應SQL中的from table_name,指定查詢的表名。
columns:對應select column1, column2, ...,指定查詢的列名稱。
selection:對應where column = value,指定查詢的行的約束條件。
selectionArgs:為selection中的占位符(?)提供具體值。
groupBy:對應group by column,指定對哪一列進行分組。
having:對應having column = value,進一步對分組后的結果進行約束。
orderBy:對應order by column1, column2, ...,指定查詢結果的排序方式。
范例:
db = mHelper.getWritableDatabase();
//查詢BOOK中所有數據
Cursor cursor = db.query("BOOK", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
//遍歷cursor,取出數據
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
//打印數據
Log.d("MySQLiteData", id + ". 《" + name + "》 " + author);
}while (cursor.moveToNext());
//關閉Cursor對象
cursor.close();
}
5. 事務
SQLite是支持事務的,那么什么是事務呢?假定需要更新大量數據,就要先刪除舊數據,而后再插入新數據。但是在過程中出現了異常,舊數據被刪除之后,新數據沒有成功插入,那這一個數據表的信息就全沒了。
事務的特性就是保證某一個系列的操作,或者全部完成,或者全部不完成。如果替換數據放在事務中進行,則要么數據替換成功,表內全部換成新數據,要么失敗,保留舊數據,不會出現刪了舊數據,但是沒插入新數據的情況。
事務的使用方法非常簡單:
// 通過SQLiteDatabase對象開啟事務
db.beginTransaction();
try {
// 在異常捕獲的代碼塊中進行具體的操作
// 在全部操作結束后將事務設置為成功
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally (
// 結束事務
db.endTransaction();
)
