1. 程式人生 > >Android 資料儲存之 SQLite資料庫儲存

Android 資料儲存之 SQLite資料庫儲存

轉載自:https://www.cnblogs.com/woider/p/5136734.html
----------------------------------------SQLite資料庫----------------------------------------------

SQLite是一款輕量級的關係型資料庫,它的運算速度非常快,佔用資源很少,通常只需要幾百K的記憶體就足夠了,因此特別適合在移動裝置上使用。

SQLite不僅支援標準的SQL語法,還遵守了資料庫的 ACID 事務,只要你以前使用過其他的關係型資料庫,就可以很快的上手SQLite。

而SQLite又比一般的資料庫要簡單的多,它甚至不用設定使用者名稱和密碼就可以使用。

Android 正是把這個功能極為強大的資料庫嵌入到了系統當中,使得本地持久化的功能有了一次質的飛躍。

-------------------------------------------建立資料庫-----------------------------------------------

Android 提供了一個SQLiteOpenHelper 幫助類,藉助這個類就可以非常簡單的對資料庫進行建立和升級。

SQLiteOpenHelper 是一個抽象類,如果需要使用它的話,就需要建立一個幫助類去繼承它。

SQLiteOpenHelper 中有兩個抽象方法,分別是 onCreate() 和 onUpdate(),需要在幫助類裡重寫這兩個方法,然後分別在這兩個方法中去實現建立和升級資料庫的邏輯。

SQLiteOpenHelper 中還有兩個非常重要的例項方法,getReadableDatabase() 和 getWritableDatabase()。這兩種方法都可以建立或開啟一個現有的資料庫(如果資料庫已存在則直接開啟,否則建立一個新的資料庫),並返回一個可對資料庫進行讀寫操作的物件。不同的是,當資料庫不可寫入的時候(如磁碟空間已滿)getReadableDatabase() 方法返回的物件將以只讀的方式去開啟資料庫,而getWritableDatabase() 方法將丟擲異常。

SQLiteOpenHelper 的構造方法接收四個引數,第一個引數是 Context,必須要有Context物件才能對資料庫進行操作。第二個引數是資料庫名,建立資料庫時使用的就是這裡指定的名稱。第三個引數允許在查詢資料庫的時候返回一個自定義的 Cursor,一般傳入null。第四個引數表示當前資料庫的版本號,可用於對資料庫進行升級操作。

構建出 SQLiteOpenHelper 的例項之後,再呼叫它的 getReadableDatabase() 或 getWritableDatabase() 方法就能夠建立資料庫了,資料庫檔案會存放在 /data/data/<包名>/database/ 目錄下。

新建 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) {

    }
}

在 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彈出。

關於如何檢視SQLite資料庫,請參考:Android 中 SQLite 資料庫的檢視

-------------------------------------------升級資料庫-----------------------------------------------

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,3);

這裡將資料庫版本號指定為3,表示我們對資料庫進行升級了。重新執行程式,並點選建立資料庫按鈕,這時就會再次彈出建立成功的提示。

升級資料庫的最佳方式:

  粗暴的刪除當前所有的表來達到更新的效果,對於使用者來說是非常糟糕的,因為以前程式中儲存的本地資料全部丟失了。其實只需進行一些合理的控制,就可以保證升級資料庫的時候資料並不會丟失了。

  每一個數據庫版本都會對應一個版本號,當指定的資料庫版本號大於當前資料庫版本號的時候,就會進入到 onUpgrade()方法中去執行更新操作。這裡需要為每一個版本號賦予它各自改變的內容,然後在 onUpgrade()方法中對當前資料庫的版本進行判斷,再執行相應的改變就可以了。

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

注意,switch 中每一個 case 的最後都是沒有使用 break 的,這是為了保證跨版本升級的時候,每一次的資料庫修改都能被全部執行。

使用這種方式來維護資料庫的升級,不管版本怎樣更新,都可以保證資料庫的表結構是最新的,而且表中的資料也完全不會丟失。

--------------------------------------使用SQL操作資料庫-------------------------------------------

現在你已經掌握了建立和升級資料庫的方法,接下來就該學習一下如何對錶中的資料進行操作了。

其實我們可以對資料庫進行的操作也就無非四種,新增(Create)、查詢(Rectrieve)、更新(Update),刪除(Delete)。

每一種操作又各自對應了一種SQL命令,新增資料時使用 insert,查詢資料時使用 select,更新資料時使用 update,刪除資料時使用 delete。

而作為一個開發者熟練使用SQL語句是非常有必要的,Android 提供了一系列方法,使得可以直接通過SQL來操作資料庫。

下面簡略演示一下,如何直接使用SQL來完成增刪改查的操作。

新增資料的方法如下:

    /**
     * SQL插入語句:
     * INSERT INTO Book(name,author,pages,price) VALUES
     * ("The Da Vinci Code" ,"Dan Brown",454,16.96);
     */
    db.execSQL("INSERT INTO Book(name,author,pages,price) VALUES(?,?,?,?",
            new String[]{"The Lost Symbol", "Dan Brown", "510", "19.95"});

刪除資料的方法如下:

    /**
     *SQL刪除語句:
     * DELETE FROM Book WHERE pages > 500;
     */
    db.execSQL("DELETE FROM Book WHERE pages > ?",new String[]{"500"});

更新資料的方法如下:

    /**
     * SQL更新語句:
     * UPDATE Book SET price = 10.99 WHERE name = "The Da Vinci Code" ;
     */
    db.execSQL("UPDATE Book SET price = ? WHERE name = ?",
            new String[]{"10.99", "The Da Vinci Code"});

查詢資料的方法如下:

    /**
     * SQL查詢語句:
     * SELECT * FROM BOOK ;
     */
    db.rawQuery("SELECT * FROM BOOK", null);

注意:除了查詢資料的時候呼叫的是 SQLiteDatabase 的 rawQuery()方法,其他的操作都是呼叫的 execSQL()方法。

--------------------------------------------新增資料------------------------------------------------

並非所有開發者都能非常熟練度使用SQL語言,因此 Android 也提供了一系列的輔助性方法,使得在 Android 中即使不去編寫 SQL 語句,也能輕鬆完成所有的增刪改查操作。

SQLiteDatabase 中提供了一個 insert() 方法,這個方法就是專門用於新增資料的。

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

        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 列設定為自動增長了,它的值會在入庫的時候自動生成,不需要手動賦值。

--------------------------------------------更新資料------------------------------------------------

SQLiteDatabase 中提供了一個非常好用的 update() 方法用於對資料進行更新,這個方法接收四個引數,第一個引數和 insert()方法一樣,也是表名,在這裡指定去更新哪張表裡的資料。第二個引數是 ContentValues 物件,把要更新的資料在這裡組裝進去。第三、第四個引數用於去約束更新某一行的資料,不指定的話預設就是更新所有行。

        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"});
        }

--------------------------------------------刪除資料------------------------------------------------

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

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

--------------------------------------------查詢資料------------------------------------------------

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 物件,查詢到的所有資料都將從這個物件中取出。

        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。