簡介:SQLite是輕量級嵌入式數(shù)據(jù)庫引擎跋涣,它支持 SQL 語言揩环,并且只利用很少的內(nèi)存就有很好的性能。此外它還是開源的颁褂,任何人都可以使用它故响。許多開源項(xiàng)目((Mozilla, PHP, Python)都使用了 SQLite.SQLite 由以下幾個(gè)組件組成:SQL 編譯器、內(nèi)核颁独、后端以及附件彩届。SQLite 通過利用虛擬機(jī)和虛擬數(shù)據(jù)庫引擎(VDBE),使調(diào)試誓酒、修改和擴(kuò)展 SQLite 的內(nèi)核變得更加方便樟蠕。
特點(diǎn):
面向資源有限的設(shè)備,
沒有服務(wù)器進(jìn)程,
所有數(shù)據(jù)存放在同一文件中跨平臺(tái)靠柑,
可自由復(fù)制寨辩。
SQLite 內(nèi)部結(jié)構(gòu):
SQLite 基本上符合 SQL-92 標(biāo)準(zhǔn),和其他的主要 SQL 數(shù)據(jù)庫沒什么區(qū)別歼冰。它的優(yōu)點(diǎn)就是高效靡狞,Android 運(yùn)行時(shí)環(huán)境包含了完整的 SQLite。
SQLite 和其他數(shù)據(jù)庫最大的不同就是對數(shù)據(jù)類型的支持隔嫡,創(chuàng)建一個(gè)表時(shí)甸怕,可以在 CREATE TABLE 語句中指定某列的數(shù)據(jù)類型甘穿,但是你可以把任何數(shù)據(jù)類型放入任何列中。當(dāng)某個(gè)值插入數(shù)據(jù)庫時(shí)梢杭,SQLite 將檢查它的類型温兼。如果該類型與關(guān)聯(lián)的列不匹配,則 SQLite 會(huì)嘗試將該值轉(zhuǎn)換成該列的類型武契。如果不能轉(zhuǎn)換募判,則該值將作為其本身具有的類型存儲(chǔ)。比如可以把一個(gè)字符串(String)放入 INTEGER 列咒唆。SQLite 稱這為“弱類型”(manifest typing.)届垫。 此外,SQLite 不支持一些標(biāo)準(zhǔn)的 SQL 功能全释,特別是外鍵約束(FOREIGN KEY constrains)敦腔,嵌套 transcaction 和 RIGHT OUTER JOIN 和 FULL OUTER JOIN, 還有一些 ALTER TABLE 功能。 除了上述功能外恨溜,SQLite 是一個(gè)完整的 SQL 系統(tǒng)符衔,擁有完整的觸發(fā)器,交易等等糟袁。
Android 集成了 SQLite 數(shù)據(jù)庫 Android 在運(yùn)行時(shí)(run-time)集成了 SQLite判族,所以每個(gè) Android 應(yīng)用程序都可以使用 SQLite 數(shù)據(jù)庫。
對于熟悉 SQL 的開發(fā)人員來時(shí)项戴,在 Android 開發(fā)中使用 SQLite 相當(dāng)簡單形帮。但是,由于 JDBC 會(huì)消耗太多的系統(tǒng)資源周叮,所以 JDBC 對于手機(jī)這種內(nèi)存受限設(shè)備來說并不合適辩撑。因此,Android 提供了一些新的 API 來使用 SQLite 數(shù)據(jù)庫仿耽,Android 開發(fā)中合冀,程序員需要學(xué)使用這些 API。
數(shù)據(jù)庫存儲(chǔ)在 data/< 項(xiàng)目文件夾 >/databases/ 下项贺。 Android 開發(fā)中使用 SQLite 數(shù)據(jù)庫 Activites 可以通過 Content Provider 或者 Service 訪問一個(gè)數(shù)據(jù)庫君躺。
下面會(huì)詳細(xì)講解如果創(chuàng)建數(shù)據(jù)庫,添加數(shù)據(jù)和查詢數(shù)據(jù)庫开缎。 創(chuàng)建數(shù)據(jù)庫 Android 不自動(dòng)提供數(shù)據(jù)庫棕叫。在 Android 應(yīng)用程序中使用 SQLite,必須自己創(chuàng)建數(shù)據(jù)庫奕删,然后創(chuàng)建表俺泣、索引,填充數(shù)據(jù)。
1.創(chuàng)建數(shù)據(jù)庫
Android 提供了 SQLiteOpenHelper 幫助你創(chuàng)建一個(gè)數(shù)據(jù)庫伏钠,你只要繼承 SQLiteOpenHelper 類侮邀,就可以輕松的創(chuàng)建數(shù)據(jù)庫。SQLiteOpenHelper 類根據(jù)開發(fā)應(yīng)用程序的需要贝润,封裝了創(chuàng)建和更新數(shù)據(jù)庫使用的邏輯。
SQLiteOpenHelper 的子類铝宵,至少需要實(shí)現(xiàn)三個(gè)方法:
1 構(gòu)造函數(shù)打掘,調(diào)用父類 SQLiteOpenHelper 的構(gòu)造函數(shù)。這個(gè)方法需要四個(gè)參數(shù):上下文環(huán)境(例如鹏秋,一個(gè) Activity)尊蚁,數(shù)據(jù)庫名字,一個(gè)可選的游標(biāo)工廠(通常是 Null)侣夷,一個(gè)代表你正在使用的數(shù)據(jù)庫模型版本的整數(shù)横朋。
2 onCreate()方法,它需要一個(gè) SQLiteDatabase 對象作為參數(shù)百拓,根據(jù)需要對這個(gè)對象填充表和初始化數(shù)據(jù)琴锭。
3 onUpgrage() 方法,它需要三個(gè)參數(shù)衙传,一個(gè) SQLiteDatabase 對象决帖,一個(gè)舊的版本號和一個(gè)新的版本號,這樣你就可以清楚如何把一個(gè)數(shù)據(jù)庫從舊的模型轉(zhuǎn)變到新的模型蓖捶。
下面示例代碼展示了如何繼承 SQLiteOpenHelper 創(chuàng)建數(shù)據(jù)庫:
public class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context, String name, CursorFactory cursorFactory, int version)
{
super(context, name, cursorFactory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO 創(chuàng)建數(shù)據(jù)庫后地回,對數(shù)據(jù)庫的操作
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO 更改數(shù)據(jù)庫版本的操作
}
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
// TODO 每次成功打開數(shù)據(jù)庫后首先被執(zhí)行
}
}
2.數(shù)據(jù)的處理
SQLiteDatabase類為我們提供了很多種方法,上面的代碼中基本上囊括了大部分的數(shù)據(jù)庫操作俊鱼;對于添加刻像、更新和刪除來說,我們都可以使用
1 db.executeSQL(String sql);
2 db.executeSQL(String sql, Object[] bindArgs);//sql語句中使用占位符并闲,然后第二個(gè)參數(shù)是實(shí)際的參數(shù)集
除了統(tǒng)一的形式之外细睡,他們還有各自的操作方法:
1 db.insert(String table, String nullColumnHack, ContentValues values);
2 db.update(String table, Contentvalues values, String whereClause, String whereArgs);
3 db.delete(String table, String whereClause, String whereArgs);
以上三個(gè)方法的第一個(gè)參數(shù)都是表示要操作的表名;insert中的第二個(gè)參數(shù)表示如果插入的數(shù)據(jù)每一列都為空的話帝火,需要指定此行中某一列的名稱纹冤,系統(tǒng)將此列設(shè)置為NULL,不至于出現(xiàn)錯(cuò)誤购公;insert中的第三個(gè)參數(shù)是ContentValues類型的變量萌京,是鍵值對組成的Map,key代表列名宏浩,value代表該列要插入的值知残;update的第二個(gè)參數(shù)也很類似,只不過它是更新該字段key為最新的value值比庄,第三個(gè)參數(shù)whereClause表示W(wǎng)HERE表達(dá)式求妹,比如“age > ? and age < ?”等乏盐,最后的whereArgs參數(shù)是占位符的實(shí)際參數(shù)值;delete方法的參數(shù)也是一樣
下面給出demo
(1)數(shù)據(jù)的添加
1.使用insert方法
1 ContentValues cv = new ContentValues();//實(shí)例化一個(gè)ContentValues用來裝載待插入的數(shù)據(jù)
2 cv.put("title","you are beautiful");//添加title
3 cv.put("weather","sun"); //添加weather
4 cv.put("context","xxxx"); //添加context
5 String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
6 .format(new Date());
7 cv.put("publish ",publish); //添加publish
8 db.insert("diary",null,cv);//執(zhí)行插入操作
2.使用execSQL方式來實(shí)現(xiàn)
String sql = "insert into user(username,password) values ('Jack Johnson','iLovePopMuisc');//插入操作的SQL語句
db.execSQL(sql);//執(zhí)行SQL語句
(2)數(shù)據(jù)的刪除
同樣有2種方式可以實(shí)現(xiàn)
String whereClause = "username=?";//刪除的條件
String[] whereArgs = {"Jack Johnson"};//刪除的條件參數(shù)
db.delete("user",whereClause,whereArgs);//執(zhí)行刪除
使用execSQL方式的實(shí)現(xiàn)
String sql = "delete from user where username='Jack Johnson'";//刪除操作的SQL語句
db.execSQL(sql);//執(zhí)行刪除操作
(3)數(shù)據(jù)修改
同上制恍,仍是2種方式
ContentValues cv = new ContentValues();//實(shí)例化ContentValues
cv.put("password","iHatePopMusic");//添加要更改的字段及內(nèi)容
String whereClause = "username=?";//修改條件
String[] whereArgs = {"Jack Johnson"};//修改條件的參數(shù)
db.update("user",cv,whereClause,whereArgs);//執(zhí)行修改
使用execSQL方式的實(shí)現(xiàn)
String sql = "update user set password = 'iHatePopMusic' where username='Jack Johnson'";//修改的SQL語句
db.execSQL(sql);//執(zhí)行修改
(4)數(shù)據(jù)查詢
下面來說說查詢操作父能。查詢操作相對于上面的幾種操作要復(fù)雜些,因?yàn)槲覀兘?jīng)常要面對著各種各樣的查詢條件净神,所以系統(tǒng)也考慮到這種復(fù)雜性何吝,為我們提供了較為豐富的查詢形式:
1 db.rawQuery(String sql, String[] selectionArgs);
2 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
3 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
4 db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
上面幾種都是常用的查詢方法,第一種最為簡單鹃唯,將所有的SQL語句都組織到一個(gè)字符串中爱榕,使用占位符代替實(shí)際參數(shù),selectionArgs就是占位符實(shí)際參數(shù)集坡慌;
各參數(shù)說明:
table:表名稱
colums:表示要查詢的列所有名稱集
selection:表示W(wǎng)HERE之后的條件語句黔酥,可以使用占位符
selectionArgs:條件語句的參數(shù)數(shù)組
groupBy:指定分組的列名
having:指定分組條件,配合groupBy使用
orderBy:y指定排序的列名
limit:指定分頁參數(shù)
distinct:指定“true”或“false”表示要不要過濾重復(fù)值
Cursor:返回值,相當(dāng)于結(jié)果集ResultSet
最后洪橘,他們同時(shí)返回一個(gè)Cursor對象跪者,代表數(shù)據(jù)集的游標(biāo),有點(diǎn)類似于JavaSE中的ResultSet熄求。下面是Cursor對象的常用方法:
1 c.move(int offset); //以當(dāng)前位置為參考,移動(dòng)到指定行
2 c.moveToFirst(); //移動(dòng)到第一行
3 c.moveToLast(); //移動(dòng)到最后一行
4 c.moveToPosition(int position); //移動(dòng)到指定行
5 c.moveToPrevious(); //移動(dòng)到前一行
6 c.moveToNext(); //移動(dòng)到下一行
7 c.isFirst(); //是否指向第一條
8 c.isLast(); //是否指向最后一條
9 c.isBeforeFirst(); //是否指向第一條之前
10 c.isAfterLast(); //是否指向最后一條之后
11 c.isNull(int columnIndex); //指定列是否為空(列基數(shù)為0)
12 c.isClosed(); //游標(biāo)是否已關(guān)閉
13 c.getCount(); //總數(shù)據(jù)項(xiàng)數(shù)
14 c.getPosition(); //返回當(dāng)前游標(biāo)所指向的行數(shù)
15 c.getColumnIndex(String columnName);//返回某列名對應(yīng)的列索引值
16 c.getString(int columnIndex); //返回當(dāng)前行指定列的值
實(shí)現(xiàn)代碼
String[] params = {12345,123456};
Cursor cursor = db.query("user",columns,"ID=?",params,null,null,null);//查詢并獲得游標(biāo)
if(cursor.moveToFirst()){//判斷游標(biāo)是否為空
for(int i=0;i<cursor.getCount();i++){
cursor.move(i);//移動(dòng)到指定記錄
String username = cursor.getString(cursor.getColumnIndex("username");
String password = cursor.getString(cursor.getColumnIndex("password"));
}
}
通過rawQuery實(shí)現(xiàn)的帶參數(shù)查詢
Cursor result=db.rawQuery("SELECT ID, name, inventory FROM mytable");
//Cursor c = db.rawQuery("s name, inventory FROM mytable where ID=?",new Stirng[]{"123456"});
result.moveToFirst();
while (!result.isAfterLast()) {
int id=result.getInt(0);
String name=result.getString(1);
int inventory=result.getInt(2);
// do something useful with these
result.moveToNext();
}
result.close();
在上面的代碼示例中坑夯,已經(jīng)用到了這幾個(gè)常用方法中的一些,關(guān)于更多的信息抡四,大家可以參考官方文檔中的說明柜蜈。
注意:最后當(dāng)我們完成了對數(shù)據(jù)庫的操作后指巡,記得調(diào)用SQLiteDatabase的close()方法釋放數(shù)據(jù)庫連接,否則容易出現(xiàn)SQLiteException藻雪。
上面就是SQLite的基本應(yīng)用,但在實(shí)際開發(fā)中指煎,為了能夠更好的管理和維護(hù)數(shù)據(jù)庫,我們會(huì)封裝一個(gè)繼承自SQLiteOpenHelper類的數(shù)據(jù)庫操作類至壤,然后以這個(gè)類為基礎(chǔ)枢纠,再封裝我們的業(yè)務(wù)邏輯方法。
下面是一個(gè)參考案例:
1、首先創(chuàng)建數(shù)據(jù)庫類
1 import android.content.Context;
2 import android.database.sqlite.SQLiteDatabase;
3 import android.database.sqlite.SQLiteDatabase.CursorFactory;
4 import android.database.sqlite.SQLiteOpenHelper;
5
6 public class SqliteDBHelper extends SQLiteOpenHelper {
7
8 // 步驟1:設(shè)置常數(shù)參量
9 private static final String DATABASE_NAME = "diary_db";
10 private static final int VERSION = 1;
11 private static final String TABLE_NAME = "diary";
12
13 // 步驟2:重載構(gòu)造方法
14 public SqliteDBHelper(Context context) {
15 super(context, DATABASE_NAME, null, VERSION);
16 }
17
18 /*
19 * 參數(shù)介紹:context 程序上下文環(huán)境 即:XXXActivity.this
20 * name 數(shù)據(jù)庫名字
21 * factory 接收數(shù)據(jù)脓斩,一般情況為null
22 * version 數(shù)據(jù)庫版本號
23 */
24 public SqliteDBHelper(Context context, String name, CursorFactory factory,
25 int version) {
26 super(context, name, factory, version);
27 }
28 //數(shù)據(jù)庫第一次被創(chuàng)建時(shí),onCreate()會(huì)被調(diào)用
29 @Override
30 public void onCreate(SQLiteDatabase db) {
31 // 步驟3:數(shù)據(jù)庫表的創(chuàng)建
32 String strSQL = "create table "
33 + TABLE_NAME
34 + "(tid integer primary key autoincrement,title varchar(20),weather varchar(10),context text,publish date)";
35 //步驟4:使用參數(shù)db,創(chuàng)建對象
36 db.execSQL(strSQL);
37 }
38 //數(shù)據(jù)庫版本變化時(shí)畴栖,會(huì)調(diào)用onUpgrade()
39 @Override
40 public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
41
42 }
43 }
正如上面所述随静,數(shù)據(jù)庫第一次創(chuàng)建時(shí)onCreate方法會(huì)被調(diào)用,我們可以執(zhí)行創(chuàng)建表的語句吗讶,當(dāng)系統(tǒng)發(fā)現(xiàn)版本變化之后燎猛,會(huì)調(diào)用onUpgrade方法,我們可以執(zhí)行修改表結(jié)構(gòu)等語句关翎。
2.Dao文件來封裝我們所有的業(yè)務(wù)方法,代碼如下:
1 import android.content.Context;
2 import android.database.Cursor;
3 import android.database.sqlite.SQLiteDatabase;
4
5 import com.chinasoft.dbhelper.SqliteDBHelper;
6
7 public class DiaryDao {
8
9 private SqliteDBHelper sqliteDBHelper;
10 private SQLiteDatabase db;
11
12 // 重寫構(gòu)造方法
13 public DiaryDao(Context context) {
14 this.sqliteDBHelper = new SqliteDBHelper(context);
15 db = sqliteDBHelper.getWritableDatabase();
16 }
17
18 // 讀操作
19 public String execQuery(final String strSQL) {
20 try {
21 System.out.println("strSQL>" + strSQL);
22 // Cursor相當(dāng)于JDBC中的ResultSet
23 Cursor cursor = db.rawQuery(strSQL, null);
24 // 始終讓cursor指向數(shù)據(jù)庫表的第1行記錄
25 cursor.moveToFirst();
26 // 定義一個(gè)StringBuffer的對象鸠信,用于動(dòng)態(tài)拼接字符串
27 StringBuffer sb = new StringBuffer();
28 // 循環(huán)游標(biāo)纵寝,如果不是最后一項(xiàng)記錄
29 while (!cursor.isAfterLast()) {
30 sb.append(cursor.getInt(0) + "/" + cursor.getString(1) + "/"
31 + cursor.getString(2) + "/" + cursor.getString(3) + "/"
32 + cursor.getString(4)+"#");
33 //cursor游標(biāo)移動(dòng)
34 cursor.moveToNext();
35 }
36 db.close();
37 return sb.deleteCharAt(sb.length()-1).toString();
38 } catch (RuntimeException e) {
39 e.printStackTrace();
40 return null;
41 }
42
43 }
44
45 // 寫操作
46 public boolean execOther(final String strSQL) {
47 db.beginTransaction(); //開始事務(wù)
48 try {
49 System.out.println("strSQL" + strSQL);
50 db.execSQL(strSQL);
51 db.setTransactionSuccessful(); //設(shè)置事務(wù)成功完成
52 db.close();
53 return true;
54 } catch (RuntimeException e) {
55 e.printStackTrace();
56 return false;
57 }finally {
58 db.endTransaction(); //結(jié)束事務(wù)
59 }
60
61 }
62 }
我們在Dao構(gòu)造方法中實(shí)例化sqliteDBHelper并獲取一個(gè)SQLiteDatabase對象,作為整個(gè)應(yīng)用的數(shù)據(jù)庫實(shí)例星立;在增刪改信息時(shí)爽茴,我們采用了事務(wù)處理,確保數(shù)據(jù)完整性绰垂;最后要注意釋放數(shù)據(jù)庫資源db.close()室奏,這一個(gè)步驟在我們整個(gè)應(yīng)用關(guān)閉時(shí)執(zhí)行,這個(gè)環(huán)節(jié)容易被忘記劲装,所以朋友們要注意胧沫。
我們獲取數(shù)據(jù)庫實(shí)例時(shí)使用了getWritableDatabase()方法,也許朋友們會(huì)有疑問占业,在getWritableDatabase()和getReadableDatabase()中绒怨,你為什么選擇前者作為整個(gè)應(yīng)用的數(shù)據(jù)庫實(shí)例呢?在這里我想和大家著重分析一下這一點(diǎn)谦疾。
我們來看一下SQLiteOpenHelper中的getReadableDatabase()方法:
1 public synchronized SQLiteDatabase getReadableDatabase() {
2 if (mDatabase != null && mDatabase.isOpen()) {
3 // 如果發(fā)現(xiàn)mDatabase不為空并且已經(jīng)打開則直接返回
4 return mDatabase;
5 }
6
7 if (mIsInitializing) {
8 // 如果正在初始化則拋出異常
9 throw new IllegalStateException("getReadableDatabase called recursively");
10 }
11
12 // 開始實(shí)例化數(shù)據(jù)庫mDatabase
13
14 try {
15 // 注意這里是調(diào)用了getWritableDatabase()方法
16 return getWritableDatabase();
17 } catch (SQLiteException e) {
18 if (mName == null)
19 throw e; // Can't open a temp database read-only!
20 Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
21 }
22
23 // 如果無法以可讀寫模式打開數(shù)據(jù)庫 則以只讀方式打開
24
25 SQLiteDatabase db = null;
26 try {
27 mIsInitializing = true;
28 String path = mContext.getDatabasePath(mName).getPath();// 獲取數(shù)據(jù)庫路徑
29 // 以只讀方式打開數(shù)據(jù)庫
30 db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);
31 if (db.getVersion() != mNewVersion) {
32 throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to "
33 + mNewVersion + ": " + path);
34 }
35
36 onOpen(db);
37 Log.w(TAG, "Opened " + mName + " in read-only mode");
38 mDatabase = db;// 為mDatabase指定新打開的數(shù)據(jù)庫
39 return mDatabase;// 返回打開的數(shù)據(jù)庫
40 } finally {
41 mIsInitializing = false;
42 if (db != null && db != mDatabase)
43 db.close();
44 }
45 }
在getReadableDatabase()方法中南蹂,首先判斷是否已存在數(shù)據(jù)庫實(shí)例并且是打開狀態(tài),如果是念恍,則直接返回該實(shí)例,否則試圖獲取一個(gè)可讀寫模式的數(shù)據(jù)庫實(shí)例峰伙,如果遇到磁盤空間已滿等情況獲取失敗的話瞳氓,再以只讀模式打開數(shù)據(jù)庫,獲取數(shù)據(jù)庫實(shí)例并返回叽赊,然后為mDatabase賦值為最新打開的數(shù)據(jù)庫實(shí)例必搞。既然有可能調(diào)用到getWritableDatabase()方法恕洲,我們就要看一下了:
public synchronized SQLiteDatabase getWritableDatabase() {
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
// 如果mDatabase不為空已打開并且不是只讀模式 則返回該實(shí)例
return mDatabase;
}
if (mIsInitializing) {
throw new IllegalStateException("getWritableDatabase called recursively");
}
// If we have a read-only database open, someone could be using it
// (though they shouldn't), which would cause a lock to be held on
// the file, and our attempts to open the database read-write would
// fail waiting for the file lock. To prevent that, we acquire the
// lock on the read-only database, which shuts out other users.
boolean success = false;
SQLiteDatabase db = null;
// 如果mDatabase不為空則加鎖 阻止其他的操作
if (mDatabase != null)
mDatabase.lock();
try {
mIsInitializing = true;
if (mName == null) {
db = SQLiteDatabase.create(null);
} else {
// 打開或創(chuàng)建數(shù)據(jù)庫
db = mContext.openOrCreateDatabase(mName, 0, mFactory);
}
// 獲取數(shù)據(jù)庫版本(如果剛創(chuàng)建的數(shù)據(jù)庫,版本為0)
int version = db.getVersion();
// 比較版本(我們代碼中的版本mNewVersion為1)
if (version != mNewVersion) {
db.beginTransaction();// 開始事務(wù)
try {
if (version == 0) {
// 執(zhí)行我們的onCreate方法
onCreate(db);
} else {
// 如果我們應(yīng)用升級了mNewVersion為2,而原版本為1則執(zhí)行onUpgrade方法
onUpgrade(db, version, mNewVersion);
}
db.setVersion(mNewVersion);// 設(shè)置最新版本
db.setTransactionSuccessful();// 設(shè)置事務(wù)成功
} finally {
db.endTransaction();// 結(jié)束事務(wù)
}
}
onOpen(db);
success = true;
return db;// 返回可讀寫模式的數(shù)據(jù)庫實(shí)例
} finally {
mIsInitializing = false;
if (success) {
// 打開成功
if (mDatabase != null) {
// 如果mDatabase有值則先關(guān)閉
try {
mDatabase.close();
} catch (Exception e) {
}
mDatabase.unlock();// 解鎖
}
mDatabase = db;// 賦值給mDatabase
} else {
// 打開失敗的情況:解鎖、關(guān)閉
if (mDatabase != null)
mDatabase.unlock();
if (db != null)
db.close();
}
}
}
大家可以看到癞谒,幾個(gè)關(guān)鍵步驟是弹砚,首先判斷mDatabase如果不為空已打開并不是只讀模式則直接返回桌吃,否則如果mDatabase不為空則加鎖苞轿,然后開始打開或創(chuàng)建數(shù)據(jù)庫搬卒,比較版本秀睛,根據(jù)版本號來調(diào)用相應(yīng)的方法蹂安,為數(shù)據(jù)庫設(shè)置新版本號田盈,最后釋放舊的不為空的mDatabase并解鎖,把新打開的數(shù)據(jù)庫實(shí)例賦予mDatabase简软,并返回最新實(shí)例痹升。
看完上面的過程之后疼蛾,大家或許就清楚了許多,如果不是在遇到磁盤空間已滿等情況衍慎,getReadableDatabase()一般都會(huì)返回和getWritableDatabase()一樣的數(shù)據(jù)庫實(shí)例稳捆,所以我們在DBManager構(gòu)造方法中使用getWritableDatabase()獲取整個(gè)應(yīng)用所使用的數(shù)據(jù)庫實(shí)例是可行的乔夯。當(dāng)然如果你真的擔(dān)心這種情況會(huì)發(fā)生驯嘱,那么你可以先用getWritableDatabase()獲取數(shù)據(jù)實(shí)例喳坠,如果遇到異常壕鹉,再試圖用getReadableDatabase()獲取實(shí)例晾浴,當(dāng)然這個(gè)時(shí)候你獲取的實(shí)例只能讀不能寫了
3.數(shù)據(jù)庫的使用脊凰,讓我們看一下如何使用這些數(shù)據(jù)操作方法來顯示數(shù)據(jù)狸涌,界面核心邏輯代碼:
public class SQLiteActivity extends Activity {
public DiaryDao diaryDao;
//因?yàn)間etWritableDatabase內(nèi)部調(diào)用了mContext.openOrCreateDatabase(mName, 0, mFactory);
//所以要確保context已初始化,我們可以把實(shí)例化Dao的步驟放在Activity的onCreate里
@Override
protected void onCreate(Bundle savedInstanceState) {
diaryDao = new DiaryDao(SQLiteActivity.this);
initDatabase();
}
class ViewOcl implements View.OnClickListener {
@Override
public void onClick(View v) {
String strSQL;
boolean flag;
String message;
switch (v.getId()) {
case R.id.btnAdd:
String title = txtTitle.getText().toString().trim();
String weather = txtWeather.getText().toString().trim();;
String context = txtContext.getText().toString().trim();;
String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date());
// 動(dòng)態(tài)組件SQL語句
strSQL = "insert into diary values(null,'" + title + "','"
+ weather + "','" + context + "','" + publish + "')";
flag = diaryDao.execOther(strSQL);
//返回信息
message = flag?"添加成功":"添加失敗";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
break;
case R.id.btnDelete:
strSQL = "delete from diary where tid = 1";
flag = diaryDao.execOther(strSQL);
//返回信息
message = flag?"刪除成功":"刪除失敗";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
break;
case R.id.btnQuery:
strSQL = "select * from diary order by publish desc";
String data = diaryDao.execQuery(strSQL);
Toast.makeText(getApplicationContext(), data, Toast.LENGTH_LONG).show();
break;
case R.id.btnUpdate:
strSQL = "update diary set title = '測試標(biāo)題1-1' where tid = 1";
flag = diaryDao.execOther(strSQL);
//返回信息
message = flag?"更新成功":"更新失敗";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
break;
}
}
}
private void initDatabase() {
// 創(chuàng)建數(shù)據(jù)庫對象
SqliteDBHelper sqliteDBHelper = new SqliteDBHelper(SQLiteActivity.this);
sqliteDBHelper.getWritableDatabase();
System.out.println("數(shù)據(jù)庫創(chuàng)建成功");
}
}
Android sqlite3數(shù)據(jù)庫管理工具
Android SDK的tools目錄下提供了一個(gè)sqlite3.exe工具般渡,這是一個(gè)簡單的sqlite數(shù)據(jù)庫管理工具。開發(fā)者可以方便的使用其對sqlite數(shù)據(jù)庫進(jìn)行命令行的操作脸秽。
程序運(yùn)行生成的.db文件一般位于"/data/data/項(xiàng)目名(包括所處包名)/databases/.db"豹储,因此要對數(shù)據(jù)庫文件進(jìn)行操作需要先找到數(shù)據(jù)庫文件:
1剥扣、進(jìn)入shell 命令
adb shell
2钠怯、找到數(shù)據(jù)庫文件
cd data/data
ls --列出所有項(xiàng)目
cd project_name --進(jìn)入所需項(xiàng)目名
cd databases
ls --列出現(xiàn)寸的數(shù)據(jù)庫文件
3晦炊、進(jìn)入數(shù)據(jù)庫
sqlite3 test_db --進(jìn)入所需數(shù)據(jù)庫
會(huì)出現(xiàn)類似如下字樣:
SQLite version 3.6.22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>
至此断国,可對數(shù)據(jù)庫進(jìn)行sql操作。
4霞捡、sqlite常用命令
.databases --產(chǎn)看當(dāng)前數(shù)據(jù)庫
.tables --查看當(dāng)前數(shù)據(jù)庫中的表
.help --sqlite3幫助
.schema --各個(gè)表的生成語句