Android SQLite數據庫使用 學習與代碼實踐
SQLiteOpenHelper 類
用SQLiteOpenHelper
類中的 getWritableDatabase()
和getReadableDatabase()
方法可以獲得數據庫的引用。
為了實現對數據庫版本進行管理,SQLiteOpenHelper
類提供了兩個重要的方法,分別是 onCreate()
和 onUpgrade()
,前者用於初次使用軟件時生成數據庫表,后者用於升級軟件時更新數據庫表結構。
當調用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法獲取用於操作數據庫的SQLiteDatabase實例的時候,如果數據庫不存在,Android系統會自動生成一個數據庫,接着調用onCreate()方法。
onCreate()方法在初次生成數據庫時才會被調用,在onCreate()方法里可以生成數據庫表結構及添加一些應用使用到的初始化數據。
onUpgrade()方法在數據庫的版本發生變化時會被調用,一般在軟件升級時才需改變版本號,而數據庫的版本是由程序員控制的。
假設數據庫現在的版本是1,由於業務的變更,修改了數據庫表結構,這時候就需要升級軟件,升級軟件時希望更新用戶手機里的數據庫表結構,為了實現這一目的,可以把原來的數據庫版本設置為2,並且在onUpgrade()方法里面實現表結構的更新。
當軟件的版本升級次數比較多,這時在onUpgrade()方法里面可以根據原版號和目標版本號進行判斷,然后做出相應的表結構及數據更新。
SQLiteDatabase類
Android提供了一個名為 SQLiteDatabase
的類(SQLiteOpenHelper
類中的 getWritableDatabase()
和getReadableDatabase()
方法返回這個類的對象)。
SQLiteDatabase
類封裝了一些操作數據庫的API,使用該類可以完成對數據進行添加(Create)、查詢(Retrieve)、更新(Update)和刪除(Delete)操作(這些操作簡稱為CRUD)。
SQLiteDatabase的學習,應該重點掌握execSQL()和rawQuery()方法。
execSQL()方法可以執行insert、delete、update和CREATE TABLE之類有更改行為的SQL語句;
rawQuery()方法用於執行select語句。
程序實例
首先是一個DatabaseHelper類,它繼承了SQLiteOpenHelper,實現了onCreate和onUpgrade方法。
雖然數據庫的名字和版本都是在該類的構造函數中傳入,但是數據庫實際被創建是在該類的getWritableDatabase()或getReadableDatabase()方法第一次被調用時。
第一次創建之后接着會調用onCreate方法(我們在這里創建了數據表),之后onCreate就不再被調用。
DatabaseHelper類:
package com.example.hellodatabases; import android.content.Context; import android.database.DatabaseErrorHandler; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; //參考:http://blog.csdn.net/liuhe688/article/details/6715983 public class DatabaseHelper extends SQLiteOpenHelper// 繼承SQLiteOpenHelper類 { // 數據庫版本號 private static final int DATABASE_VERSION = 1; // 數據庫名 private static final String DATABASE_NAME = "TestDB.db"; // 數據表名,一個數據庫中可以有多個表(雖然本例中只建立了一個表) public static final String TABLE_NAME = "PersonTable"; // 構造函數,調用父類SQLiteOpenHelper的構造函數 public DatabaseHelper(Context context, String name, CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { super(context, name, factory, version, errorHandler); } public DatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // SQLiteOpenHelper的構造函數參數: // context:上下文環境 // name:數據庫名字 // factory:游標工廠(可選) // version:數據庫模型版本號 } public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); // 數據庫實際被創建是在getWritableDatabase()或getReadableDatabase()方法調用時 Log.d(AppConstants.LOG_TAG, "DatabaseHelper Constructor"); // CursorFactory設置為null,使用系統默認的工廠類 } // 繼承SQLiteOpenHelper類,必須要覆寫的三個方法:onCreate(),onUpgrade(),onOpen() @Override public void onCreate(SQLiteDatabase db) { // 調用時間:數據庫第一次創建時onCreate()方法會被調用 // onCreate方法有一個 SQLiteDatabase對象作為參數,根據需要對這個對象填充表和初始化數據 // 這個方法中主要完成創建數據庫后對數據庫的操作 Log.d(AppConstants.LOG_TAG, "DatabaseHelper onCreate"); // 構建創建表的SQL語句(可以從SQLite Expert工具的DDL粘貼過來加進StringBuffer中) StringBuffer sBuffer = new StringBuffer(); sBuffer.append("CREATE TABLE [" + TABLE_NAME + "] ("); sBuffer.append("[_id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "); sBuffer.append("[name] TEXT,"); sBuffer.append("[age] INTEGER,"); sBuffer.append("[info] TEXT)"); // 執行創建表的SQL語句 db.execSQL(sBuffer.toString()); // 即便程序修改重新運行,只要數據庫已經創建過,就不會再進入這個onCreate方法 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 調用時間:如果DATABASE_VERSION值被改為別的數,系統發現現有數據庫版本不同,即會調用onUpgrade // onUpgrade方法的三個參數,一個 SQLiteDatabase對象,一個舊的版本號和一個新的版本號 // 這樣就可以把一個數據庫從舊的模型轉變到新的模型 // 這個方法中主要完成更改數據庫版本的操作 Log.d(AppConstants.LOG_TAG, "DatabaseHelper onUpgrade"); db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); // 上述做法簡單來說就是,通過檢查常量值來決定如何,升級時刪除舊表,然后調用onCreate來創建新表 // 一般在實際項目中是不能這么做的,正確的做法是在更新數據表結構時,還要考慮用戶存放於數據庫中的數據不丟失 } @Override public void onOpen(SQLiteDatabase db) { super.onOpen(db); // 每次打開數據庫之后首先被執行 Log.d(AppConstants.LOG_TAG, "DatabaseHelper onOpen"); } }
定義一個Person類作為測試數據:
package com.example.hellodatabases; public class Person { public int _id; public String name; public int age; public String info; public Person() { } public Person(String name, int age, String info) { this.name = name; this.age = age; this.info = info; } }
之后是一個管理類,持有數據庫對象,對各種操作進行了封裝:
package com.example.hellodatabases; import java.util.ArrayList; import java.util.List; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; //參考:http://blog.csdn.net/liuhe688/article/details/6715983 public class DBManager { private DatabaseHelper helper; private SQLiteDatabase db; public DBManager(Context context) { Log.d(AppConstants.LOG_TAG, "DBManager --> Constructor"); helper = new DatabaseHelper(context); // 因為getWritableDatabase內部調用了mContext.openOrCreateDatabase(mName, 0, // mFactory); // 所以要確保context已初始化,我們可以把實例化DBManager的步驟放在Activity的onCreate里 db = helper.getWritableDatabase(); } /** * add persons * * @param persons */ public void add(List<Person> persons) { Log.d(AppConstants.LOG_TAG, "DBManager --> add"); // 采用事務處理,確保數據完整性 db.beginTransaction(); // 開始事務 try { for (Person person : persons) { db.execSQL("INSERT INTO " + DatabaseHelper.TABLE_NAME + " VALUES(null, ?, ?, ?)", new Object[] { person.name, person.age, person.info }); // 帶兩個參數的execSQL()方法,采用占位符參數?,把參數值放在后面,順序對應 // 一個參數的execSQL()方法中,用戶輸入特殊字符時需要轉義 // 使用占位符有效區分了這種情況 } db.setTransactionSuccessful(); // 設置事務成功完成 } finally { db.endTransaction(); // 結束事務 } } /** * update person's age * * @param person */ public void updateAge(Person person) { Log.d(AppConstants.LOG_TAG, "DBManager --> updateAge"); ContentValues cv = new ContentValues(); cv.put("age", person.age); db.update(DatabaseHelper.TABLE_NAME, cv, "name = ?", new String[] { person.name }); } /** * delete old person * * @param person */ public void deleteOldPerson(Person person) { Log.d(AppConstants.LOG_TAG, "DBManager --> deleteOldPerson"); db.delete(DatabaseHelper.TABLE_NAME, "age >= ?", new String[] { String.valueOf(person.age) }); } /** * query all persons, return list * * @return List<Person> */ public List<Person> query() { Log.d(AppConstants.LOG_TAG, "DBManager --> query"); ArrayList<Person> persons = new ArrayList<Person>(); Cursor c = queryTheCursor(); while (c.moveToNext()) { Person person = new Person(); person._id = c.getInt(c.getColumnIndex("_id")); person.name = c.getString(c.getColumnIndex("name")); person.age = c.getInt(c.getColumnIndex("age")); person.info = c.getString(c.getColumnIndex("info")); persons.add(person); } c.close(); return persons; } /** * query all persons, return cursor * * @return Cursor */ public Cursor queryTheCursor() { Log.d(AppConstants.LOG_TAG, "DBManager --> queryTheCursor"); Cursor c = db.rawQuery("SELECT * FROM " + DatabaseHelper.TABLE_NAME, null); return c; } /** * close database */ public void closeDB() { Log.d(AppConstants.LOG_TAG, "DBManager --> closeDB"); // 釋放數據庫資源 db.close(); } }
主要的Activity:

package com.example.hellodatabases; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.database.Cursor; import android.database.CursorWrapper; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.SimpleCursorAdapter; //參考:http://blog.csdn.net/liuhe688/article/details/6715983 public class HelloDBActivity extends Activity { private DBManager dbManager; private ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hello_db); listView = (ListView) findViewById(R.id.listView); // 初始化DBManager dbManager = new DBManager(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.hello_db, menu); return true; } @Override protected void onDestroy() { super.onDestroy(); dbManager.closeDB();// 釋放數據庫資源 } public void add(View view) { ArrayList<Person> persons = new ArrayList<Person>(); Person person1 = new Person("Ella", 22, "lively girl"); Person person2 = new Person("Jenny", 22, "beautiful girl"); Person person3 = new Person("Jessica", 23, "sexy girl"); Person person4 = new Person("Kelly", 23, "hot baby"); Person person5 = new Person("Jane", 25, "a pretty woman"); persons.add(person1); persons.add(person2); persons.add(person3); persons.add(person4); persons.add(person5); dbManager.add(persons); } public void update(View view) { // 把Jane的年齡改為30(注意更改的是數據庫中的值,要查詢才能刷新ListView中顯示的結果) Person person = new Person(); person.name = "Jane"; person.age = 30; dbManager.updateAge(person); } public void delete(View view) { // 刪除所有三十歲以上的人(此操作在update之后進行,Jane會被刪除(因為她的年齡被改為30)) // 同樣是查詢才能查看更改結果 Person person = new Person(); person.age = 30; dbManager.deleteOldPerson(person); } public void query(View view) { List<Person> persons = dbManager.query(); ArrayList<Map<String, String>> list = new ArrayList<Map<String, String>>(); for (Person person : persons) { HashMap<String, String> map = new HashMap<String, String>(); map.put("name", person.name); map.put("info", person.age + " years old, " + person.info); list.add(map); } SimpleAdapter adapter = new SimpleAdapter(this, list, android.R.layout.simple_list_item_2, new String[] { "name", "info" }, new int[] { android.R.id.text1, android.R.id.text2 }); listView.setAdapter(adapter); } @SuppressWarnings("deprecation") public void queryTheCursor(View view) { Cursor c = dbManager.queryTheCursor(); startManagingCursor(c); // 托付給activity根據自己的生命周期去管理Cursor的生命周期 CursorWrapper cursorWrapper = new CursorWrapper(c) { @Override public String getString(int columnIndex) { // 將簡介前加上年齡 if (getColumnName(columnIndex).equals("info")) { int age = getInt(getColumnIndex("age")); return age + " years old, " + super.getString(columnIndex); } return super.getString(columnIndex); } }; // 確保查詢結果中有"_id"列 SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursorWrapper, new String[] { "name", "info" }, new int[] { android.R.id.text1, android.R.id.text2 }); ListView listView = (ListView) findViewById(R.id.listView); listView.setAdapter(adapter); } }
另,附上布局和其他:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="add" android:text="add" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="update" android:text="update" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="delete" android:text="delete" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="query" android:text="query" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="queryTheCursor" android:text="queryTheCursor" /> <ListView android:id="@+id/listView" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>

package com.example.hellodatabases; public class AppConstants { public static final String LOG_TAG="Hello DB"; }
參考資料
官網Training: Saving Data in SQL Databases
http://developer.android.com/training/basics/data-storage/databases.html
Android中SQLite應用詳解:
http://blog.csdn.net/liuhe688/article/details/6715983
關於Cursor類的介紹:
http://www.cnblogs.com/TerryBlog/archive/2010/07/05/1771459.html
Android 小項目之--SQLite 使用法門 (附源碼):
http://www.cnblogs.com/TerryBlog/archive/2010/06/12/1757166.html