1、資料類型的問題
SQLite內部只支援NULL、INTEGER、REAL(浮點數)、TEXT(文本)和BLOB(大二進位對象)這5種資料類型,但實際上SQLite完全可以接受varchar(n)、char(n)、decimal(p,s)等資料類型,只不過SQLite會在原酸或儲存時將他們轉換為上面5種資料類型中相應的類型。
除此之外,SQLite還有一個特點:它允許把各種類型的資料儲存到任何類型欄位中,開發人員可以不用關心聲明該欄位所使用的資料類型。例如程式可以把字串類型的值存入到INTEGER類型的欄位中,也可以把數值資料類型的值存入到布爾類型的欄位中。。。。但是有一中情況例外:定義為INTEGER PRIMARY KEY的欄位只能儲存64位整數,當向這種欄位儲存除整數意外的其他類型的資料時,SQLite會產生錯誤。
並且,可以向字串欄位中插入任意長度的字串,即使聲明varchar(20)加以限制。
2、資料庫何時關閉問題
我們使用SQLiteOpenHelper擷取一個SQLiteDatabase資料庫物件,然後就可以去使用這個資料庫了。那麼這個資料庫何時關閉呢。一般我們不需要操作一次資料庫,就去關閉它,因為這樣很耗資源。畢竟這不是像J2EE上面的資料庫,J2EE伺服器上的資料庫會根據訪問的使用者產生許多資料庫連接,所以需要關閉。但是在Android手機上,只有我們當前的應用程式再回有一個資料庫的串連,當然不需要用一次關閉一次。
所以,我們只需要完成了所有的資料庫操作,或者退出程式的時候,選擇一個恰當的實際關閉。
3、SQLiteDatabase對象的擷取問題。
我們擷取資料庫執行個體時使用了getWritableDatabase()方法,也許朋友們會有疑問,在getWritableDatabase()和getReadableDatabase()中,你為什麼選擇前者作為整個應用的資料庫執行個體呢?在這裡我想和大家著重分析一下這一點。
我們來看一下SQLiteOpenHelper中的getReadableDatabase()方法:
public synchronized SQLiteDatabase getReadableDatabase() { if (mDatabase != null && mDatabase.isOpen()) { // 如果發現mDatabase不為空白並且已經開啟則直接返回 return mDatabase; } if (mIsInitializing) { // 如果正在初始化則拋出異常 throw new IllegalStateException("getReadableDatabase called recursively"); } // 開始執行個體化資料庫mDatabase try { // 注意這裡是調用了getWritableDatabase()方法 return getWritableDatabase(); } catch (SQLiteException e) { if (mName == null) throw e; // Can't open a temp database read-only! Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e); } // 如果無法以可讀寫入模式開啟資料庫 則以唯讀方式開啟 SQLiteDatabase db = null; try { mIsInitializing = true; String path = mContext.getDatabasePath(mName).getPath();// 擷取資料庫路徑 // 以唯讀方式開啟資料庫 db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY); if (db.getVersion() != mNewVersion) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + mNewVersion + ": " + path); } onOpen(db); Log.w(TAG, "Opened " + mName + " in read-only mode"); mDatabase = db;// 為mDatabase指定新開啟的資料庫 return mDatabase;// 返回開啟的資料庫 } finally { mIsInitializing = false; if (db != null && db != mDatabase) db.close(); } }
在getReadableDatabase()方法中,首先判斷是否已存在資料庫執行個體並且是開啟狀態,如果是,則直接返回該執行個體,否則試圖擷取一個可讀寫入模式的資料庫執行個體,如果遇到磁碟空間已滿等情況擷取失敗的話,再以唯讀模式開啟資料庫,擷取資料庫執行個體並返回,然後為mDatabase賦值為最新開啟的資料庫執行個體。既然有可能調用到getWritableDatabase()方法,我們就要看一下了:
public synchronized SQLiteDatabase getWritableDatabase() { if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) { // 如果mDatabase不為空白已開啟並且不是唯讀模式 則返回該執行個體 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 { // 開啟或建立資料庫 db = mContext.openOrCreateDatabase(mName, 0, mFactory); } // 擷取資料庫版本(如果剛建立的資料庫,版本為0) int version = db.getVersion(); // 比較版本(我們代碼中的版本mNewVersion為1) if (version != mNewVersion) { db.beginTransaction();// 開始事務 try { if (version == 0) { // 執行我們的onCreate方法 onCreate(db); } else { // 如果我們應用升級了mNewVersion為2,而原版本為1則執行onUpgrade方法 onUpgrade(db, version, mNewVersion); } db.setVersion(mNewVersion);// 設定最新版本 db.setTransactionSuccessful();// 設定事務成功 } finally { db.endTransaction();// 結束事務 } } onOpen(db); success = true; return db;// 返回可讀寫入模式的資料庫執行個體 } finally { mIsInitializing = false; if (success) { // 開啟成功 if (mDatabase != null) { // 如果mDatabase有值則先關閉 try { mDatabase.close(); } catch (Exception e) { } mDatabase.unlock();// 解鎖 } mDatabase = db;// 賦值給mDatabase } else { // 開啟失敗的情況:解鎖、關閉 if (mDatabase != null) mDatabase.unlock(); if (db != null) db.close(); } } }
大家可以看到,幾個關鍵步驟是,首先判斷mDatabase如果不為空白已開啟並不是唯讀模式則直接返回,否則如果mDatabase不為空白則加鎖,然後開始開啟或建立資料庫,比較版本,根據版本號碼來調用相應的方法,為資料庫設定新版本號碼,最後釋放舊的不為空白的mDatabase並解鎖,把新開啟的資料庫執行個體賦予mDatabase,並返回最新執行個體。
看完上面的過程之後,大家或許就清楚了許多,如果不是在遇到磁碟空間已滿等情況,getReadableDatabase()一般都會返回和getWritableDatabase()一樣的資料庫執行個體,所以我們在DBManager構造方法中使用getWritableDatabase()擷取整個應用所使用的資料庫執行個體是可行的。當然如果你真的擔心這種情況會發生,那麼你可以先用getWritableDatabase()擷取資料執行個體,如果遇到異常,再試圖用getReadableDatabase()擷取執行個體,當然這個時候你擷取的執行個體只能讀不能寫了。
並且,同一個資料庫只會返回一個SQLiteDatabase資料庫執行個體。
4、在Android中對資料庫動作陳述式和方法
SQLite中sql語句和MySql非常類似,完全可以去參照MySql的sql語句。