Android提供操作SQLite的能力,但實際操作的是一個SQLiteDataBase物件,而SQLiteOpenHelper類別的任務是負責管理資料庫檔案的建立與版本控制。
SQLiteOpenHelper(context, database name, factory, database version)
SQLiteOpenHelper負責建立一個Helper物件來建立、開啟與管理一個SQLiteDatabase。在實作Helper類別時,name通常使用的是一個資料庫檔案(StudentList.db),也是一個組合過的路徑(/data/data/SQLite/StudentList.db)
onCreate(SQLiteDatabase db)
資料庫建立時所啟動執行的方法,代表會重新建立一個新的資料庫,通常在此階段把資料庫基本的Schema、Table或相關設定一併處理完畢。此方法在執行時,如遇到DB已存在,則進入onOpen()方法;如不存在則會先建立一個DataBase。
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
此方法發生在當資料庫版本不一致時就會觸發。主要任務為因為在知道資料庫版本不同時,要進行資料的覆蓋或重建都必需要好好考量,因為有可能讓資料遺失或不一致。
onOpen(SQLiteDatabase db)
當資料庫被開啟時則進入此Method,配合SQLiteOpenHelper提供的二個方法相呼應:getReadableDatabase()與getWritableDatabase(),分別取得唯讀(Read-only)與可編輯(Write)的資料庫實體。然而,這二個方法即時觸發onCreate、onUpgrade或onOpen的來源者,所以上述介紹的幾個重要的方法,則真正被呼叫時是在getWritableDatabase()或是getReadableDatabase()被使用時。
Android程式實際操作的真正實體(database),提供所有操作SQLite的功能,包括:新增、修改、刪除、查詢等,協助操作裡面的資料,SQLiteDataBase提供一些已經包裝好的方法,當然也包括了指定SQL指令來操作資料的內容,除此之外,也支援Transcation的使用。beginTransaction()、setTransactionSuccessful()、endTransaction()
execSQL(String sql)
專門執行單一非Select的SQL指令或是其他操作資料庫的指令,例如:建立資料表的指令、或是操作權限等相關的管理類的指令。
rawQuery(String sql, String[] selectionArgs)
如果今天針對要操作的資料,不單單只有一個Table而已時,即可以透過這個方法就像過去操作.NET的SQLCommand物件方式,把SQL指令先撰寫好,交由SQLiteDatabase來操作資料。回傳值為:Cursor(其中也可以使用SQLiteCursor,它是Cursor的實作類別)。
query/insert(String table, String nullColumnHack, ContentValues values)/update/delete
這是包裝好針對一個Table進行基本的新增、修改、刪除與查詢的固定方法,其使方法非常容易,透過指定的Table與相關的參數,包括:select的欄位、where的參數(使用[?] 做為參數取代值)、Sort等,讓開發人員可以快速取得資料,但如果需要比較複雜的查詢,就建議使用上述的rawQuery()來執行。它一樣回傳一個Cursor物件。至於新增、修改與刪除的部分呢,將配合下方的物件來使用。
在新增、修改、刪除的方法使用時,如果是使用上述已包裝好的方法,例如:insert(String table, String nullColumnHack, ContentValues values),則一定要了解ContentValues與ContenResolver的使用方法。並且,這三個方法的使用,都會回傳成功影響的比數,如失敗則為:-1。
ContentValues
專門用於欲操作的資料內容。透過put的方式操作內容值,這與過去直接操作data table或data row是比較不同的。不過它相似於key/value的操作方式,因此,當在使用put(String key, object value)時,key則需要對應到table的column name才行。
Cursor
則是操作查詢出來的結果,提供了相當豐富的方法與屬性,協助開發人員擷取資料的內容與移動索引值。
它是實作Cursor的一個類別,主要承接從SQLiteDatabase查詢出來的資料,它被擷取出來之後,則與資料庫內的資料採不同步的方式,這也就是說,當query出來的結果被放入SQLiteCursor之後,它就像是一個Cache,後來如果其他程式段改了DB中的資料,則SQLiteCursor並不會被更新,需要重新查詢(requery())才有辦法取得。然而,SQLiteCursor類別提供很多便利的方法,包括:getCount()取得欄位數、getDatabase()取得來源資料庫等方法,操作起來如同使用.NET中的TableRow與TableColumn的使用概念。
該類別提供一個事先編譯(pre-compiled)SQL指令,讓該SQL指令可以重覆針對SQLiteDatabase使用時,不需要每次都重新編譯,常見的例子:連續性新增資料。但要特別注意的:"它無法回傳多筆資料(rows),只能1對1的資料集合被回傳"。另外,在使用它時,不需要new,只需直接使用compileStatement(String)語法即可。它也是非同步的物件,要同步都需要自己實作。
如果透過String.format來組合SQL指令覺得不方便的話,可以使用SQLiteQueryBuiler類別來進行查詢指定的建立。它提供透過設定屬性與方法的方式,組合要查詢的相關SQL指令(buildQuery)。也有提供豐富的, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String)" target=_blank>, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String)" target=_blank>, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String)" target=_blank>buildUnionSubQuery等方法
在 Android 你可以透過 SQLite 來進行資料庫的存取。但是在使用前必需先自己實作 SQLiteOpenHelper 類別。在建立一個新的空白的class之後,加入「extends SQLiteOpenHelper」指令,系統就會要求建立基本的方法,依照系統指示所建立的程式框架如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package moke.test; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class SQLite extends SQLiteOpenHelper { public SQLite(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub //context=內容物件;name=傳入資料庫名稱;factory=複雜查詢時使用;version=資料庫版本 } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } } |
由於通常一支程式只會使用一個資料庫,因此我們可以省略建構子傳入的部份參數。此外,為了讓所有的方法都存取相同的資料庫,必需加入 SQLiteDatabase 物件。修改後的建構子如下:
8 9 10 11 12 13 14 15 16 |
private static final String DATABASE_NAME = "moke_config.db"; //資料庫名稱 private static final int DATABASE_VERSION = 1; //資料庫版本 private SQLiteDatabase db; public SQLite(Context context) { //建構子 super(context, DATABASE_NAME, null, DATABASE_VERSION); db = this.getWritableDatabase(); } |
接下來要實作 onCreate 與 onUpgrade 方法。onCreate 是當資料庫建立時會執行的方法,我們可以在這裡建立資料表或設定預設的資料庫參數。onUpgrade 是當資料庫的版本不一致時,會執行的方法。如果新舊版本的資料表格式不同,有二種處理方式。第一種是將舊的資料表刪除,直接建立新的資料表,但是這樣原有的資料就會消失,因此第二種方法是將舊的資料表按照新的表格格式儲存,存完之後再刪除舊的。這裡的範例先用最簡單的方式來撰寫,直接刪除舊的資料表。完整的 SQLiteOpenHelper 實作程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package moke.test; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class SQLite extends SQLiteOpenHelper { private static final String DATABASE_NAME = "moke_config.db"; //資料庫名稱 private static final int DATABASE_VERSION = 1; //資料庫版本 private SQLiteDatabase db; public SQLite(Context context) { //建構子 super(context, DATABASE_NAME, null, DATABASE_VERSION); db = this.getWritableDatabase(); } @Override public void onCreate(SQLiteDatabase db) { String DATABASE_CREATE_TABLE = "create table config (" + "_ID INTEGER PRIMARY KEY," + "name TEXT," + "value INTEGER" + ");"; //建立config資料表,詳情請參考SQL語法 db.execSQL(DATABASE_CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //oldVersion=舊的資料庫版本;newVersion=新的資料庫版本 db.execSQL("DROP TABLE IF EXISTS config"); //刪除舊有的資料表 onCreate(db); } } |
「private SQLiteDatabase db;」物件變數,透過這個 db 變數可以對資料庫進行增刪改查的操作。
查詢
進行資料庫查詢時,必需建立一個方法回傳 Cursor 物件。查詢的方式有兩種。
1 2 3 4 |
// 取得所有記錄 public Cursor getAll() { return db.rawQuery("SELECT * FROM table_name", null); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// 取得所有記錄 public Cursor getAll() { return db.query("table_name", //資料表名稱 new String[] {"_ID", "name", "value"}, //欄位名稱 null, // WHERE null, // WHERE 的參數 null, // GROUP BY null, // HAVING null // ORDOR BY ); } // 取得一筆紀錄 public Cursor get(long rowId) throws SQLException { Cursor cursor = db.query(true, "table_name", //資料表名稱 new String[] {"_ID", "name", "value"}, //欄位名稱 "_ID=" + rowId, //WHERE null, // WHERE 的參數 null, // GROUP BY null, // HAVING null, // ORDOR BY null // 限制回傳的rows數量 ); // 注意:不寫會出錯 if (cursor != null) { cursor.moveToFirst(); //將指標移到第一筆資料 } return cursor; } |
新增
1 2 3 4 5 6 7 8 |
//新增一筆記錄,成功回傳rowID,失敗回傳-1 public long create(String name, String value) { ContentValues args = new ContentValues(); args.put("name", name); args.put("value", value); return db.insert("table_name", null, args); } |
刪除
1 2 3 4 5 6 7 |
//刪除記錄,回傳成功刪除筆數 public int delete(long rowId) { return db.delete("table_name", //資料表名稱 "_ID=" + rowId, //WHERE null //WHERE的參數 ); } |
修改
1 2 3 4 5 6 7 8 9 10 11 |
//修改記錄,回傳成功修改筆數 public int update(long rowId, String value) { ContentValues args = new ContentValues(); args.put("value", value); return db.update("table_name", //資料表名稱 args, //VALUE "_ID=" + rowId, //WHERE null //WHERE的參數 ); } |
從SQLite類別裡抓回來的資料是 Cursor 物件,可以把它想像成一張資料表,基本的操作方式如下(詳細方法請參考官方網站):
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
private SQLite dbHelper; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); dbHelper = new SQLite(this); Cursor cursor = dbHelper.getAll(); //取得SQLite類別的回傳值:Cursor物件 int rows_num = cursor.getCount(); //取得資料表列數 if(rows_num != 0) { cursor.moveToFirst(); //將指標移至第一筆資料 for(int i=0; i<rows_num; i++) { int id = cursor.getInt(0); //取得第0欄的資料,根據欄位type使用適當語法 String name = cursor.getString(1); int value = cursor.getInt(2); cursor.moveToNext(); //將指標移至下一筆資料 } } cursor.close(); //關閉Cursor dbHelper.close(); //關閉資料庫,釋放記憶體 } |
如果是使用 ListActivity 讀取資料庫,則使用的語法如下):
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
private SQLite dbHelper; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 無記錄 getListView().setEmptyView(findViewById(R.id.empty)); // 有記錄 dbHelper = new SQLite(this); Cursor cursor = dbHelper.getAll(); //取得SQLite類別的回傳值:Cursor物件 startManagingCursor(cursor); String[] from = new String[]{"name", "value"}; //讀取資料庫欄位 int[] to = new int[]{android.R.id.text1, android.R.id.text2}; //目標ListView欄位名稱(固定) SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursor, from, to); setListAdapter(adapter); } |
REF:http://www.moke.tw/wordpress/computer/advanced/177
