1. 程式人生 > >SQLite資料庫精煉詳解

SQLite資料庫精煉詳解

一、前期基礎知識儲備

Android本地化儲存三種方式:
①檔案儲存,儲存簡單二進位制資料和文字資料;
②SharedPreference儲存,鍵值對儲存,儲存資料型別多樣,儲存方式單一;
③SQLite資料庫儲存,Android內建資料庫,儲存複雜關係型資料,質的飛躍。
在這裡插入圖片描述
SQLite正式釋出於2000年,現在Android內建的是SQLite3.0。佔用幾百KB資源,但運算速度飛快,非常適合在移動裝置上使用。SQLite資料庫使用時會涉及到三個類:
①SQLiteOpenHelper:Android系統提供的操作SQLite資料庫的方式。工具類 抽象類,我們通過繼承該類,然後重寫資料庫建立以及更新的方法,我們還可以通過該類的物件獲得資料庫例項或者關閉資料庫。
②SQliteDatabase

:平常資料庫操作方式使用的類,資料庫訪問類——我們可以通過該類的物件來直接使用操作資料庫的方式對資料庫做增刪改查的操作。(見用於,熟悉資料庫語法的大牛)
③Cursot:遊標,有點類似於JDBC裡的resultset,結果集!可以簡單理解為指向資料庫中某一記錄的指標。使用可以同平常資料庫一樣,也可以使用Android系統提供的操作方式。
本篇部落格,主要分析Android系統內建的操作資料庫的方式,所以打交道的類只有兩個:SQLiteOpenHelper和Cursot。

二、上程式碼,具體分析

1)使用SQLiteOpenHelper建立資料庫

①SQLiteOpenHelper 是一個抽象類

,如果需要使用它的話,就需要建立一個幫助類去繼承它;
②抽象父類SQLiteOpenHelper 中有兩個抽象方法:onCreate()onUpdate(),需要在子類裡重寫這兩個方法,然後分別在這兩個方法中去實現建立和升級資料庫的邏輯;
新建 MyDatabaseHelper 類繼承自 SQLiteOpenHelper,程式碼如下:

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "CREATE TABLE book ("
            + "id  integer PRIMARY KEY Autoincrement ,"
            + "author text ,"
            + "price real ,"
            + "pages integer,"
            + "name text )";
    /**
     * integer:整形
     * real:浮點型
     * text:文字型別
     * blob:二進位制型別
     * PRIMARY KEY將id列設定為主鍵
     * AutoIncrement關鍵字表示id列是自動增長的
     */

    private Context myContent;

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        myContent = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //建立資料庫的同時建立Book表
        db.execSQL(CREATE_BOOK);
        //提示資料庫建立成功
        Toast.makeText(myContent, "資料庫建立成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

③SQLiteOpenHelper 中另外兩個非常重要的例項方法,getReadableDatabase() (查)和 getWritableDatabase()(增刪改)。這兩種方法都可以建立或開啟一個現有的資料庫(如果資料庫已存在則直接開啟,否則建立一個新的資料庫),並返回一個可對資料庫進行增刪改查操作的物件;
④子類需要重寫父類SQLiteOpenHelper 的兩個構造方法,第二個構造方法接收四個引數,第一個引數是 Context,必須要有Context物件才能對資料庫進行操作。第二個引數是資料庫名,建立資料庫時使用的就是這裡指定的名稱。第三個引數允許在查詢資料庫的時候返回一個自定義的 Cursor,一般傳入null。第四個引數表示當前資料庫的版本號,可用於對資料庫進行升級操作。

構建出 SQLiteOpenHelper 的例項之後,再呼叫它的 getReadableDatabase() 或 getWritableDatabase() 方法就能夠建立資料庫了,資料庫檔案會存放在 /data/data/<包名>/database/ 目錄下。利用Android Studio右下角工具可直接檢視。

在 MainActivity 中進行測試,程式碼如下:

public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //構建一個 MyDatabaseHelper 物件,通過建構函式將資料庫名指定為 BookStore.db
        dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,1);
        Button createDatabase = (Button)findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /**
                 *呼叫getWritableDatabase() 方法
                 * 自動檢測當前程式中 BookStore.db 這個資料庫
                 * 如果不存在則建立該資料庫並呼叫 onCreate() 方法
                 * 同時Book表也會被建立
                 */
                dbHelper.getWritableDatabase();
            }
        });
    }
}

點選按鈕 BookStore.db 資料庫和 Book 表就已經建立成功了,再次點選不會再有Toast彈出。

2)使用SQLiteOpenHelper升級資料庫

onUpdate() 方法是用於對資料庫進行升級的,它在整個資料庫的管理工作中擔當著非常重要的作用。
目前 BookStore.db 中已經有一張 Book 表用於存放書的各種詳細資料,接下來再新增一張 Category 表用於記錄書籍的分類。將建表語句新增到 MyDatabaseHelper 中,程式碼如下:

    public static final String CREATE_CATEGORY = "CREATE TABLE category ("
            + "id integer PRIMARY KEY Autoincrement , "
            + "category_name text , "
            + "category_code integer )";

並在 onCreate()方法中新增:db.execSQL( CREATE_CATEGORY); 這條語句,執行程式,並不會彈出建立成功的提示。因為此時 BookStore.db 資料庫已經存在了,之後不論怎樣點選建立按鈕,MyDatabaseHelper 中的 onCreate() 方法都不會再次執行,因此新新增的表也就無法得到建立了。
只需要巧妙的運用 SQLiteOpenHelper 的升級功能就可以很輕鬆的解決這個問題。
修改 MyDatabaseHelper 中的程式碼,如下所示:

 @Override
    public void onCreate(SQLiteDatabase db) {
        //建立Book表和Category表
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
        //提示資料庫建立成功
        Toast.makeText(myContent, "資料庫建立成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /**
         * 如果發現數據庫中已經存在 Book 表或 Category 表
         * 就將這兩張表刪除掉,然後呼叫 onCreate() 方法重新建立
         * 如果在建立表時發現表已經存在,就會直接報錯
         */
        db.execSQL("DROP TABLE IF EXISTS Book");
        db.execSQL("DROP TABLE IF EXISTS Category");
        onCreate(db);
    }

接下來的問題就是如何讓 onUpgrade() 方法能夠得到執行了,還記得 SQLiteOpenHelper 的構造方法裡接受的第四個引數嗎?它表示當前資料庫的版本號,之前我們傳入的是1,現在只要傳入一個比1大的數,就可以讓 onUpgrade() 方法得到執行了,修改MainActivity 中的程式碼:

dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);

這裡將資料庫版本號指定為2,表示我們對資料庫進行升級了。重新執行程式,並點選建立資料庫按鈕,這時就會再次彈出建立成功的提示。
升級資料庫的最佳方式:
粗暴的刪除當前所有的表來達到更新的效果,對於使用者來說是非常糟糕的,因為以前程式中儲存的本地資料全部丟失了。其實只需進行一些合理的控制,就可以保證升級資料庫的時候資料並不會丟失了。

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
            case 1:
                db.execSQL(CREATE_CATEGORY);
            default:
        }
    }

每一個數據庫版本都會對應一個版本號,當指定的資料庫版本號大於當前資料庫版本號的時候,就會進入到 onUpgrade()方法中去執行更新操作。這裡需要為每一個版本號賦予它各自改變的內容,然後在 onUpgrade()方法中對當前資料庫的版本進行判斷,再執行相應的改變就可以了。
注意,switch 中每一個 case 的最後都是沒有使用 break 的,這是為了保證跨版本升級的時候,每一次的資料庫修改都能被全部執行。使用這種方式來維護資料庫的升級,不管版本怎樣更新,都可以保證資料庫的表結構是最新的,而且表中的資料也完全不會丟失。

3)使用SQLiteOpenHelper升級資料庫

注:這裡使用的Android內建的一系列輔助方法,即使用SQLiteOpenHelper操作資料庫,並不是使用SQliteDatabase直接操作資料表。
①新增資料-insert()
SQLiteDatabase 中提供了一個 insert() 方法,用於新增資料。
insert() 方法接收三個引數,第一個引數是表名,我們希望向哪張表裡新增資料,這裡就傳入該表的名字。第二個引數用於在未指定新增資料的情況下給某些可為空的列自動賦值NULL,一般用不到這個功能,直接傳入 null 即可。第三個引數是一個 ContentValues 物件,它提供了一系列的 put() 方法過載,用於向 ContentValues 中新增資料,只需要將表中的每個列名及相對應的待新增資料傳入即可。
MainActivity中程式碼如下:

            public void onClick(View v) {
                //獲取 SQLiteDatabase 物件
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                //使用ContentValues 對資料進行組裝
                ContentValues values = new ContentValues();
                //開始組裝第一條資料
                values.put("name", "The Da Vinci Code");
                values.put("author", "Dan Brown");
                values.put("pages", 454);
                values.put("price", 16.96);
                //插入第一條資料
                db.insert("Book", null, values);
                values.clear();
                //開始組裝第二條資料
                values.put("name", "The Lost Symbol");
                values.put("author", "Dan Brown");
                values.put("pages", 510);
                values.put("price", 19.95);
                //插入第二條資料
                db.insert("Book", null, values);
            }

這裡只對Book表裡其中四列的資料進行了組裝,id並沒有賦值。因為建立表的時候就將 id 列設定為自動增長了,它的值會在入庫的時候自動生成,不需要手動賦值。
②更新資料-update()
SQLiteDatabase 中提供了一個非常好用的 update() 方法用於對資料進行更新,這個方法接收四個引數,第一個引數和 insert()方法一樣,也是表名,在這裡指定去更新哪張表裡的資料。第二個引數是 ContentValues 物件,把要更新的資料在這裡組裝進去。第三、第四個引數用於去約束更新某一行的資料,不指定的話預設就是更新所有行。
MainActivity中程式碼如下:

            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price", 10.99);
                //?是一個佔位符,通過字串陣列為每個佔位符指定相應的內容
                db.update("Book", values, "name = ?", new String[]{"The Da Vinci Code"});
            }

③刪除資料-delete()
SQLiteDatabase 中提供了一個 delete()方法專門用於刪除資料,這個方法接收三個引數,第一個引數仍然是表名,第二、第三個引數用於約束刪除某一行或某幾行的資料,不指定的話預設就是刪除所有行。
MainActivity中程式碼如下:

            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                db.delete("Book", "pages > ?", new String[]{"500"});
            }

④查詢資料-query(),會使用到第三個類Cursor遊標類
SQLiteDatabase 中提供了一個 query() 方法用於對資料進行查詢。這個方法的引數非常複雜,最短的一個方法過載也需要傳入七個引數。
query()方法引數及對應SQL:

table:指定查詢的表名,對應 from table_name
columns:指定查詢的列名,對應 select column1,column2 …
selection:指定 where 的約束條件,where column = value
selectionArgs:為 where 中的佔位符提供具體的值
groupBy:指定需要分組的列,group by column
having:對分組後的結果進一步約束,having column = value
orderBy:指定查詢結果的排序方式,order by column

雖然 query()方法的引數非常多,但是不必每條查詢語句都指定上所有的引數,多數情況下只需傳入少數幾個引數就可以完成查詢操作了。
呼叫 query()方法後會返回一個 Cursor 物件,查詢到的所有資料都將從這個物件中取出。
MainActivity中程式碼如下:

            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                //查詢Book表中的所有資料
                Cursor cursor = db.query("Book", null, null, null, null, null, null, null);
                //遍歷Curosr物件,取出資料並列印
                while (cursor.moveToNext()) {
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    String author = cursor.getString(cursor.getColumnIndex("author"));
                    int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                    double price = cursor.getDouble(cursor.getColumnIndex("price"));
                    Log.d("woider", "Book Name:" + name + " Author:" 
                            + author + " Pages:" + pages + " Price:" + price);
                }
                //關閉Cursor
                cursor.close();
            }

執行 query()方法之後會得到一個 Cursor物件,然後通過 Cursor 物件的 moveToNext()方法去遍歷查詢到的每一行資料。在這個迴圈中可以通過 Cursor 的 getColumnIndex() 方法獲取到某一列在表中對應位置的索引,然後將這個索引傳入到相應的取值方法中,就可以得到從資料庫中讀取到的資料了。
最後別忘了呼叫 close()方法來關閉 Cursor。(不關閉Cursor會造成記憶體洩漏)
遊標Cursor的相關操作:
遍歷遊標:

        while(c.moveToNext()){
              int id=c.getInt(c.getColumnName(列名ID));
              String name=c.getString(c.getColumnIndex(欄位名NAME));
        }

常用方法:

(1)move(int offset);將指標上下移動offset個記錄。若offset值為正數則向下移動,若為負數則向上移動。
(2)boolean moveToFirst();指標移至結果集的第一個記錄。若移動成功則返回true,否則返回false。
(3)boolean moveToLast();指標移至結果集的最後一個記錄。若移動成功則返回true,否則返回false。
(4)boolean moveToNext();指標向後移動一條記錄,若移動成功則返回true,否則返回false。
(5)boolean moveToPrevious();指標向前移動一條記錄,若移動成功則返回true,否則返回false。
(6)isLast();若指標在Cursor結果集的最後一條記錄,則返回true,否則返回false。
(7)isAfterLast();若指標在Cursor結果集最後一條記錄之後,則返回true,否則返回false。
(8)isFirst();若指標在Cursor結果集的第一條記錄,則返回true,否則返回false。
(9)isBeforeFirst();若指標在Cursor結果集的第一條記錄之前,則返回true,否則返回false。
(10)isClose();Cursot物件是否關閉,若關閉則返回true,否則返回false。
(11)int getColumnIndex(String columnName);獲得指定列的索引值。
(12)String getString(int columnIndex);按列的索引獲取字串型別的資料。

--------------------------------------------------SQLiteOpenHelper分割線----------------------------------------------------

4)SQLiteDatabase資料庫相關操作

上面的資料庫操作都是利用SQLiteOpenHelper進行的,有興趣的讀者也可嘗試下直接使用SQLite自帶的資料庫語言進行資料庫的操作。
SQLiteDatabase資料庫類:提供了針對資料庫的增 刪 改 查的操作。
注:除了Cursor遊標物件、SQLiteDatabase資料庫物件也需要關閉。
SQLiteDatabase db=openOrCreateDatabase(“students.db”,MODE_PRIVATE,null);如果資料庫不存在則執行建立操作,存在則執行開啟操作。
db.execSQL(“create table if not exists student (_id integer primary key autoincrement,name varchar(20),age integer)”);建立資料表,如果存在則不建立。
db.execSQL(“insert into student(_id,name,age)values(null,?,?)”,new Object[]{“張立冬”,25});插入資料;
db.execSQL(“delete from student where name=‘張立冬’”);刪除資料;
db.execSQL(“update student set name=?,age=?”,new Object[]{“小明”,18});修改資料;

    Cursor c=db.rawQuery("select * from student",null);查詢資料——返回遊標(結果集);
    while(c.moveToNext()){將遊標移到下一行,存在資料返回true,不存在資料返回false;
         int id=c.getInt(c.getColumnIndex("_id"));獲得對應列名中該行的整型資料;
         String name=c.getString(c.getColumnIndex("name"));獲得對應列名中該行的字串資料;
         int age=c.getInt(c.getColumnIndex("age"));獲得對應列名中該行的整型資料
}

注:增刪改使用的是execSQL()方法,查詢用的是rawQuery()方法。