SQLite總結

SQLite Database
SQLite is an embedded relational database engine. Its developers call it a self-contained, serverless, zero-configuration and transactional SQL database engine. 其實只需要記住SQLite不需要數據庫服務器,不需要額外配置,其數據庫本身就是一個文件的一個小型數據庫。 通常情況下在android中創建數據庫,系統會在"DATA/data/APP_NAME/databases".目錄爲我們創建數據庫文件。
SQLite BasicDataTypes
SQLite支持的數據類型有很多,但他們總共可以分爲以下幾類:
  1. desc <table> //查看錶結構 
  2. select * from <table> //查詢所有更
  3. select , from table ;//查看指定列
  4. select distinct , from table ;//非重複查詢
  5. insert into users(_id,username,password) select * from users;//複製
  6. select username from users where username like 'S%' ;//非重名字首字母爲大寫S的用戶
  7. select username from users where username like '__S%' ;//非重名字第三個字母爲大寫S的用戶
  8. select * from users where _id in(001,220,230);
  9. select * from user order by _id;//以id的順序排列
  10. select * from user order by _id desc;//以id反的順序排
複製代碼

常用Select語句
  1. desc <table> //查看錶結構 
  2. select * from <table> //查詢所有更
  3. select , from table ;//查看指定列
  4. select distinct , from table ;//非重複查詢
  5. insert into users(_id,username,password) select * from users;//複製
  6. select username from users where username like 'S%' ;//非重名字首字母爲大寫S的用戶
  7. select username from users where username like '__S%' ;//非重名字第三個字母爲大寫S的用戶
  8. select * from users where _id in(001,220,230);
  9. select * from user order by _id;//以id的順序排列
  10. select * from user order by _id desc;//以id反的順序排
複製代碼

圖形環境
很少有人直接在程序當中直接編寫SQL語句,麻煩且容易出錯。所以最好應該先在一個圖形化的環境把SQL語句寫好,測試好再加入到代碼當中。 而且,SQLite數據庫本身就是一個獨立的文件,很容易從模擬器上抓取出來。可以使用圖形化界面進行分析,和修改 etc.
SQLite expert 是個簡單小巧的SQLite數據庫管理軟件,有免費版和,收費版兩種,一般來講免費版足以應付我們需求。
sqliteexpert官方網址
軟件界面截圖
使用SQLite expert 創建一張table,並讓它爲我們生成所需的sql語句
下面的例子,我們創建一張表用來存儲用戶的用戶名和密碼;數據庫名"users",表名"user_accounts",有三個列:row_id INTEGER自增類型的主鍵,usernameTEXT類型 用來保存用戶名,passwordTEXT類型,用來保存用戶密碼;
創建表格
爲了創建一張table 首先要有一個數據庫文件,點擊"File"->""New Database然後選擇文件的路徑,點擊OK便創建了一個數據庫文件 創建完數據庫之後創建表格,右擊數據庫文件名選擇"New Table"如下圖所示:
這時我們便進入了"設計狀態",填寫表名(user_accounts),點擊"Add",在Name中填寫"row_id",Type選擇Integer,如下圖所示:
因爲row_id這個列比較特殊是我們的索引的主鍵,所以還要點擊"Index",之後會看到下圖:
左邊的Available Fields 中有我們剛剛創建的row_id,點擊"Add"把row_id加入右邊的Index Fields當中。這時上面的Primary和AutoIncrement會變成可先狀態 ,勾選Primary和AutoIncrement點擊OK;
再回到Field當中創建其它兩列,分別爲username類型爲TEXT,password類型也爲TEXT,但不需要創建Index; 完成之後點擊Apply,這樣我們便使用SQLite expert創建一張表格,點擊DDL,我們可以看到SQLite expert已經爲我們生成好了生成這張表格所需的SQL語句:
在android中需要代碼生成表格時,只要把這段代碼複製過去,就可以了。
增加用戶
點擊SQL,執行以下SQL語句,爲表格增加一個用戶:
  1. INSERT INTO user_accounts(row_id,username,password) VALUES(001,'John','abcdef');
複製代碼
點擊Data會發現數據庫裏邊增加了一個用戶名爲John的用戶。爲了練習,我們不妨再增加兩個用戶,David和Sarah
  1. INSERT INTO user_accounts(row_id,username,password) VALUES(002,'David','123456');

  2. INSERT INTO user_accounts(row_id,username,password) VALUES(003,'Sarah','00000000');
複製代碼
刪除用戶
執行下面的語句刪除用戶David:
  1. DELETE FROM user_accounts WHERE username = 'David';
複製代碼
修改密碼
執行以下語句修改Sarah的密碼:
  1. update user_accounts SET password='666666' WHERE username = 'Sarah';
複製代碼
查看所用戶信息可以使用如下語句查看錶內所有用戶的信息:
  1. SELECT * FROMM user_accounts;
複製代碼
一般來講select * 的語句只在測試的時候時候,在正式代碼中不推薦使用。
查看指定列的內容執行以下語句查看所有用戶的用戶名和密碼:
  1. SELECT username,password FROM user_accounts;
複製代碼
這時個就發現row_id列沒有顯示出來。
查詢特定條件的信息
SQL可以通過給定查詢條件進行精確查找,比如我們只需要John的密碼。就可以使用這樣的語句
  1. SELECT password FROM user_accounts WHERE username = 'John';
複製代碼
以下流程圖,來源於SQLite官方文檔
Create Table Statement
column-def
type-name
column-constraint
table-constraint
foriegn-key-clause
Insert Statement
Delete Statement
qualified-table-name
Update Statement
qualified-table-name
Select Statement
select-core
result-column
join-source
single-source
join-op
join-constraint
ordering-term
compound-operator
Dealing with large amount of data分頁當數據庫數據量很大時,分頁顯示是個很簡單且符合人們習慣的一種處理方法。
獲取數據行總數:
最簡單的方法是:SELECT所有的行,再調用curosr.getCount() ;取得行數,但這樣效率會很低。 SQLite爲我們提供了一個函數很容易查出總共有多少行。有個名爲cet6_table的表格,我們想知道總共有多少行的word;可以使用如下 語句來完成:
  1. SELECT count(word) AS number FROM cet6_table;
複製代碼


count()函數爲我們返回一個Int整形,代表有多少行數據。返回的列的名字叫count(word),爲了方便閱讀和處理用as number給這個列取個 別名number;
  1. SELECT [word],[explanation] FROM cet6_table ORDER BY word LIMIT 100 OFFSET 200"
複製代碼
上語句,返回自第200行的最多100條數據。分頁時我們只要修改offset 後面的數據即可取得對應頁面的數據。
詳情見示例程序!
多線程自帶數據庫
有些程序比如,字典,輸入法等程序會在apk中帶有數據庫。思路很簡單,把預先準備好的數據庫放入asset或者raw目錄當中 使用時複製到對應的手機目錄上;再打開。以下代碼會把raw目錄中的名爲cet6的數據庫文件寫入sd卡根目錄並打開。 對於一些常用或者比較重要的數據建議放在程序的私有目錄當中。
  1. public void init(Context context) {
  2.     try {
  3.         String databasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
  4.         String databaseFilename = databasePath + "/" + DATABASE_FILENAME;
  5.         File dir = new File(databasePath);
  6.         if (!dir.exists()){
  7.             dir.mkdir();
  8.         }
  9.         if (!(new File(databaseFilename)).exists()) {
  10.             InputStream is = context.getResources().openRawResource(
  11.                     R.raw.cet6);
  12.             FileOutputStream fos = new FileOutputStream(databaseFilename);
  13.             byte[] buffer = new byte[1024];
  14.             int count = 0;
  15.             while ((count = is.read(buffer)) > 0) {
  16.                 fos.write(buffer, 0, count);
  17.             }
  18.             fos.flush();
  19.             fos.close();
  20.             is.close();
  21.         }
  22.         mCet6Db = SQLiteDatabase.openOrCreateDatabase(databaseFilename,
  23.                 null);
  24.     } catch (Exception e) {
  25.         e.printStackTrace();
  26.     }
  27. }
複製代碼
單詞提示,IME使用AutoCompleteText + SQLite很容易實現類似於baidu ,Google等搜索時出現的提示文字。 輸入法也是同樣的原理,以拼音或五筆等作爲索引,非常容易實現。可以加一個列標明一個字被選擇的次數, 在select的時候可以按照這個列來排序,這樣可以保存用戶的習慣;作出來的輸入法將更人性化。因爲輸入法的查詢量比較大 可以考慮把數據庫文件放入內存當中,SQLite本身是支持內存數據庫的。但android中封裝上沒有提供這一功能,而且SQLiteDataBase類 很多核心功能都是通過JNI實現的,所以要把數據庫搬入內存還需編寫c/c++的代碼。也可以考慮使用Lucene的全文搜索功能來實現。Date
SQLite有很多函數支持日期的方便處理。
  1. %d         day of month: 00
  2. %f         fractional seconds: SS.SSS
  3. %H         hour: 00-24
  4. %j         day of year: 001-366
  5. %J         Julian day number
  6. %m         month: 01-12
  7. %M         minute: 00-59
  8. %s         seconds since 1970-01-01
  9. %S         seconds: 00-59
  10. %w         day of week 0-6 with Sunday==0
  11. %W         week of year: 00-53
  12. %Y         year: 0000-9999
  13. %%         %
複製代碼
SQLite支持以下格式的日期:
  1. YYYY-MM-DD
  2. YYYY-MM-DD HH:MM
  3. YYYY-MM-DD HH:MM:SS
  4. YYYY-MM-DD HH:MM:SS.SSS
  5. YYYY-MM-DDTHH:MM   //T 就是字母T,用於分開日期和時間
  6. YYYY-MM-DDTHH:MM:SS
  7. YYYY-MM-DDTHH:MM:SS.SSS
  8. HH:MM
  9. HH:MM:SS
  10. HH:MM:SS.SSS
  11. now
  12. DDDDDDDDDD //is the Julian day number expressed as a floating point value.
複製代碼
strftime('%Y-%m-%d', column_name)方法可以很方便的對日期進行格式化,而不用Java對String進行再處理。
ContentValues 不支持直接把datetime('now')之類函數作爲值插入數據庫。 比如想把當前系統時間插件數據庫使用ContentValues:
  1. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
  2. Date date = new Date();
  3. ContentValues initialValues = new ContentValues(); 
  4. initialValues.put("date_created", dateFormat.format(date));
  5. long rowId = mDb.insert(DATABASE_TABLE, null, initialValues);
複製代碼
或者如附帶程序記事本程序一樣使用exeSQL(sql);
SqliteOpenHelper
上流程圖來自O'REILLY 視頻公開課"Developing Android Applications with Java"中的視頻截圖
SqliteOpenHelper是android框架爲我們提供的一個數據庫助手類。對於數據庫的操作一般都是從這個類繼承。它主要負責數據庫的創建,打開,更新 等一些操作。
Singleton DAO
很多有j2ee背景的開發者都喜歡建立一個單例模式的數據庫操作類,專門處理數據庫之間的交流。也有很多人把這個習慣帶到的Android的開發當中。 而且我們也經常需要在幾個Activity,Service同時訪問一個數據庫文件,所以經常能看到類似於這樣的代碼:
  1. public class DbAccess {

  2.     class DbHelper extends SQLiteOpenHelper {
  3.     //此處代碼被省略
  4.     }
  5.     private static DB_NAME = "users";
  6.     private mDbHelper = null;
  7.     private static mDbAccess;//to make sure there is only one instance exist;
  8.     
  9.     private DbAccess(Context context){
  10.         //private constructer;
  11.         if(mDbHelper == null){
  12.             mDbHelper = new DbHelper(context,DB_NAME);
  13.         }
  14.     }
  15.     
  16.     public DbAccess getInstance(Context context){
  17.         if(mDbAccess == null){
  18.             mDbAccess = new DbAccess(context);
  19.         }
  20.         return mDbAccess;
  21.     }
  22.     
  23.     //部分代碼被省略
  24.     //insert
  25.     //delete
  26.     //update
  27.     //query
  28. }

  29. public class ActivityA extends Activity{
  30.     @Override
  31.     protected void onCreate(Bundle savedInstanceState) {
  32.         super.onCreate(savedInstanceState);
  33.         dao = DbAccess.getInstance(this);
  34.         ////部分代碼被省略
  35.     }
  36.     ////部分代碼被省略
  37. }
複製代碼
程序的流程應該是這樣的:
假設ActivityA創建了Dao的對象,又開啓了ServiceA,A使用了一段時間再跳轉到了ActivityB與B再接着使用同一個Dao對象。ActivityA創建Dao對象的時候 把自己的引用向上轉型成一個Context對象,Dao又用這個context創建了SQLiteOpenHelper,所以SQLiteOpenHelper就持有了ActivityA的引用。當ActivityA被銷燬 之後因爲單例的Dao保留有ActivityA的引用,所以ActivityA不能被GC所回收,造成整個Acitivity內存的泄漏。
解決方案
1,最簡單的,不使用單例模式,因爲SQLiteOpenHelper保證了數據庫的單例,所以實際上我們只是創建了多個SQLiteOpenHelper的對象,數據庫 對象本身還是單例的。缺點:會有一些重複的代碼,並生成多個SQLiteOpenHelper的子類對象。
2,把Dao對象放入Application當中,在Application的onCreate()中使用Application的Context進行初始化。優點,Application對象的創建在Activity和Service之前 所以保證了,Activity和Service等等使用時數據庫已經準備好了。缺點:Application的生命週期是最長的,並且Android會盡量不讓程序關閉,也就是儘量不 去銷燬Application對象,所以數據庫一直保持打開狀態,依靠Application的onDestroy()關閉數據庫,不確定的因素比較大,並且因爲打開數據庫的時候直接 影響程序啓動時間。
3,先看一個問題,爲什麼SQLiteOpenHelper需要一個Context對象?查看SQLiteOpenHelper的源碼發現如下代碼:
  1. SQLiteDatabase db = null;
  2. try {
  3.     mIsInitializing = true;
  4.     String path = mContext.getDatabasePath(mName).getPath();
  5.     db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);
  6.     if (db.getVersion() != mNewVersion) {
  7.         throw new SQLiteException("Can't upgrade read-only database from version " +
  8.         db.getVersion() + " to " + mNewVersion + ": " + path);
  9.     }
  10.     onOpen(db);
  11.     Log.w(TAG, "Opened " + mName + " in read-only mode");
  12.     mDatabase = db;
複製代碼
所以我們可以自己仿照SQLiteOpenHelper寫一個不需要Context的助手類,大constructer當中把路徑傳進行,而不是一個context對象。缺點:需要考慮多線程同步等 很多因素,有一定的複雜度。
4,在創建Dao對象時,不使用Activity的引用作爲Context,而是把Application作爲Context傳入Dao的構造函數中進行創建。優點,不會影響程序的啓動時間。 缺點:對於數據庫的關閉還是不確定。
5,在第四種方案的基礎上加入計數器。第調用getInstance計數器自增,再加入一個release()方法,計數器自減,當計數器爲0時,關閉數據庫(或者當計數器爲零且 過一段時間沒有連接時關閉)。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章