第一行代碼讀書筆記 6 -- 持久化技術(shù)(下)

本篇文章主要介紹以下幾個知識點:

  • SQLite 數(shù)據(jù)庫存儲;
  • 開源數(shù)據(jù)庫框架 LitePal。
圖片來源于網(wǎng)絡

6.3 SQLite 數(shù)據(jù)庫存儲

前面介紹的兩種存儲方式很難應付得了需要存儲大量復雜的關(guān)系型數(shù)據(jù)的時候,而使用數(shù)據(jù)庫就可以做到。

SQLite 是一款輕量級的關(guān)系型數(shù)據(jù)庫,它的運算速度非常快, 占用資源很少,通常只需幾百 K 的內(nèi)存就足夠了,因而特別適合在移動設備上使用。SQLite 不僅支持標準的 SQL 語法,還遵循了數(shù)據(jù)庫的 ACID 事務,只要你以前用過其他的關(guān)系型數(shù)據(jù)庫,就可以很快地上手 SQLite。

6.3.1 創(chuàng)建數(shù)據(jù)庫

Android 為了讓我們更加方便地管理數(shù)據(jù)庫,專門提供了一個 SQLiteOpenHelper 幫助類,借助這個類可以非常簡單地對數(shù)據(jù)庫進行創(chuàng)建和升級。

SQLiteOpenHelper 是一個抽象類,意味著若要使用它的話, 就需要創(chuàng)建一個自己的幫助類去繼承它并且重寫它的兩個抽象方法,即 onCreate() 和 onUpgrade(),去實現(xiàn)創(chuàng)建、升級數(shù)據(jù)庫的邏輯。

SQLiteOpenHelper 中有兩個重要的實例方法 , getReadableDatabase() 和 getWritableDatabase()。它們都可以創(chuàng)建或打開一個現(xiàn)有的數(shù)據(jù)庫(若已存在則打開,否則創(chuàng)建一個新的),并返回一個可對數(shù)據(jù)庫進行讀寫操作的對象。不同的是,當數(shù)據(jù)庫不可寫入的時候(如磁盤空間已滿)getReadableDatabase()方法返回的對象將以只讀的方式去打開數(shù)據(jù)庫,而 getWritableDatabase()方法則將出現(xiàn)異常。

SQLiteOpenHelper 中有兩個構(gòu)造方法可供重寫,一般使用參數(shù)少一點的那個構(gòu)造方法即可。構(gòu)建出 SQLiteOpenHelper 的實例之后,再調(diào)用它的 getReadableDatabase() 或 getWritableDatabase() 方法就能夠創(chuàng)建數(shù)據(jù)庫了,數(shù)據(jù)庫文件會存放在/data/data/<package name>/databases/目錄下。 此時,重寫的 onCreate() 方法也會得到執(zhí)行,在這里去處理一些創(chuàng)建表的邏輯。

說了這么多,還是舉例子實在。這里我們希望創(chuàng)建一個名為 BookStore.db 的數(shù)據(jù)庫,然后在這個數(shù)據(jù)庫中新建一張 book表,表中有 id(主鍵)、作者、價格、頁數(shù)和書名等列。

首先,在你的項目中新建 MyDatabaseHelper類繼承自 SQLiteOpenHelper,代碼如下所示:

/**
 * 數(shù)據(jù)庫幫助類
 * Created by KXwon on 2016/12/16.
 */

public class MyDatabaseHelper extends SQLiteOpenHelper {

    // Book表的建表語句
    private static final String CREATE_BOOK = "create table book("
            +"id integer primary key autoincrement,"
            +"author text,"
            +"price real,"
            +"pages integer,"
            +"name text)";

    private Context mContext;

    /**
     * 構(gòu)造方法
     * @param context
     * @param name 數(shù)據(jù)庫名
     * @param factory 允許我們在查詢數(shù)據(jù)的時候返回一個自定義的 Cursor,一般都是傳入 null
     * @param version 當前數(shù)據(jù)庫的版本號,可用于對數(shù)據(jù)庫進行升級操作
     */
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    /**
     * 創(chuàng)建數(shù)據(jù)庫
     * @param db
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 執(zhí)行建表語句
        db.execSQL(CREATE_BOOK);
        ToastUtils.showShort("創(chuàng)建成功");
    }

    /**
     * 升級數(shù)據(jù)庫
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

然后修改布局 activity_sqlite.xml 中代碼,添加個按鈕來創(chuàng)建數(shù)據(jù)庫:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="15dp"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btn_create_database"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="創(chuàng)建數(shù)據(jù)庫"/>

</LinearLayout>

最后修改 activity 中的代碼:

public class SQLiteActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);

        // 構(gòu)建MyDatabaseHelper對象,指定數(shù)據(jù)庫名為"BookStore.db、版本號為1
        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);

        Button btn_create_database = (Button) findViewById(R.id.btn_create_database);
        btn_create_database.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 創(chuàng)建或打開一個現(xiàn)有的數(shù)據(jù)庫(已存在則打開,否則創(chuàng)建一個新的)
                dbHelper.getWritableDatabase();
            }
        });
    }
}

上述代碼,當?shù)谝淮吸c擊按鈕時,會檢測到當前程序中并沒有 BookStore.db 這個數(shù)據(jù)庫,于是會創(chuàng)建該數(shù)據(jù)庫并調(diào)用 MyDatabaseHelper中的 onCreate()方法,創(chuàng)建 book 表,然后彈出一個 Toast 提示創(chuàng)建成功。再次點擊按鈕時,會發(fā)現(xiàn)此時已存在 BookStore.db 數(shù)據(jù)庫了,因此不會再創(chuàng)建一次。

運行代碼,點擊按鈕,結(jié)果如下:

創(chuàng)建數(shù)據(jù)表成功

下面檢查 book 表是否創(chuàng)建成功(木有興趣可跳過)。

adb 是 Android SDK 中自帶的一個調(diào)試工具,使用這個工具可以直接對連接在電腦上的手機或模擬器進行調(diào)試操作。它存放在 sdk 的 platform-tools 目錄下,如果想要在命令行中使用這個工具,就要先把它的路徑配置到環(huán)境變量里。

配置好環(huán)境變量后,就可以使用 adb 工具了。打開命令行界面,輸入 adb shell,進入到設備的控制臺,如下:

進入設備的控制臺

然后使用 cd 命令進行到/data/data/com.wonderful.myfirstcode/databases/目錄下,并使用 ls命令查看到該目錄里的文件,如下:

查看數(shù)據(jù)庫文件

此目錄下出現(xiàn)了兩個數(shù)據(jù)庫文件,一個是我們創(chuàng)建的 BookStore.db ,而另一個 BookStore.db-journal 則是為了讓數(shù)據(jù)庫能夠支持事務而產(chǎn)生的臨時日志文件,通常情況下這個文件的大小都是 0 字節(jié)。

接下來借助 sqlite 命令來打開數(shù)據(jù)庫,鍵入 sqlite3,后面加上數(shù)據(jù)庫名即可,如下:

打開BookStore.db 數(shù)據(jù)庫

這時已經(jīng)打開了 BookStore.db 數(shù)據(jù)庫,現(xiàn)在可以對這個數(shù)據(jù)庫中的表進行管理了。 首先來看一下目前數(shù)據(jù)庫中有哪些表,鍵入.table 命令,如下:

查看表

可以看到,此時數(shù)據(jù)庫中有兩張表,android_metadata 表是每個數(shù)據(jù)庫中都會自動生成的,不用管它,而另外一張 book 表就是我們在 MyDatabaseHelper 中創(chuàng)建的了。這里還可以通過.schema 命令來查看它們的建表語句,如下:

查看建表語句

由此證明,BookStore.db 數(shù)據(jù)庫和 book 表已經(jīng)創(chuàng)建成功了。之后鍵入.exit 或.quit命令可以退出數(shù)據(jù)庫的編輯,再鍵入 exit 命令就可以退出設備控制臺了。

6.3.2 升級數(shù)據(jù)庫

MyDatabaseHelper 中的onUpgrade() 方法是用于對數(shù)據(jù)庫進行升級的,它在整個數(shù)據(jù)庫的管理工作當中起著非常重要的作用。

目前項目中已經(jīng)有一張 book 表用于存放書的各種詳細數(shù)據(jù),若我們想再添加一張 category 表用于記錄書籍的分類該怎么做呢?

修改 MyDatabaseHelper 中的代碼,如下所示:

/**
 * 數(shù)據(jù)庫幫助類
 * Created by KXwon on 2016/12/16.
 */

public class MyDatabaseHelper extends SQLiteOpenHelper {

    // Book表的建表語句
    private static final String CREATE_BOOK = "create table book("
            +"id integer primary key autoincrement,"
            +"author text,"
            +"price real,"
            +"pages integer,"
            +"name text)";

    // Category表的建表語句
    private static final String CREATE_CATEGORY = "create table category("
            +"id integer primary key autoincrement,"
            +"category_name text,"
            +"category_code integer)";
    
    private Context mContext;

    /**
     * 構(gòu)造方法
     * @param context
     * @param name 數(shù)據(jù)庫名
     * @param factory 允許我們在查詢數(shù)據(jù)的時候返回一個自定義的 Cursor,一般都是傳入 null
     * @param version 當前數(shù)據(jù)庫的版本號,可用于對數(shù)據(jù)庫進行升級操作
     */
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    /**
     * 創(chuàng)建數(shù)據(jù)庫
     * @param db
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 執(zhí)行建表語句
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
        ToastUtils.showShort("創(chuàng)建成功");
    }

    /**
     * 升級數(shù)據(jù)庫
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 若發(fā)現(xiàn)數(shù)據(jù)庫中已存在 book 表或 category 表,將這兩張表刪除掉
        db.execSQL("drop table if exists book");
        db.execSQL("drop table if exists category");
        // 重新創(chuàng)建表
        onCreate(db);
    }
}

接著修改 activity 中的代碼,在 SQLiteOpenHelper 的構(gòu)造方法里接收的第四個參數(shù)傳入一個比之前傳入的版本號 1 大的數(shù),就可以讓 onUpgrade()方法得到執(zhí)行了。如下所示:

public class SQLiteActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);

        // 構(gòu)建MyDatabaseHelper對象,指定數(shù)據(jù)庫名為"BookStore.db、版本號為1
        //dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
        // 將數(shù)據(jù)庫版本號指定為2
        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);

        Button btn_create_database = (Button) findViewById(R.id.btn_create_database);
        btn_create_database.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 創(chuàng)建或打開一個現(xiàn)有的數(shù)據(jù)庫(已存在則打開,否則創(chuàng)建一個新的)
                dbHelper.getWritableDatabase();
            }
        });
    }
}

現(xiàn)在重新運行程序,并點擊按鈕,這時就會再次彈出創(chuàng)建成功的提示。

為了驗證一下 category 表是 不是已經(jīng)創(chuàng)建成功了,我們在 adb shell 中打開 BookStore.db 數(shù)據(jù)庫,然后鍵入.table 命令, 結(jié)果如下:

查看新增表

接著鍵入.schema 命令查看一下建表語句,如下:

查看新增建表語句

由此表明,category 表已創(chuàng)建成功了,同時也說明升級功能起作用了。

6.3.3 增刪查改

接下來學習一下如何對表中的數(shù)據(jù)進行操作。對數(shù)據(jù)進行的操作無非四種,即 CRUD:添加(Create),查詢(Retrieve),更新(Update),刪除(Delete)。SQLiteOpenHelper 中的 getReadableDatabase() 或 getWritableDatabase() 方法都會返回一個 SQLiteDatabase對象,借助這個對象可以對數(shù)據(jù)進行 CRUD 操作。

首先修改下布局 activity_sqlite.xml 的代碼如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="15dp"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btn_create_database"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="創(chuàng)建數(shù)據(jù)庫"/>

    <Button
        android:id="@+id/btn_add_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="添加數(shù)據(jù)"/>

    <Button
        android:id="@+id/btn_update_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="更新數(shù)據(jù)"/>

    <Button
        android:id="@+id/btn_delete_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="刪除數(shù)據(jù)"/>

    <Button
        android:id="@+id/btn_query_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查詢數(shù)據(jù)"/>

</LinearLayout>

修改后是這樣的:

修改后的布局

6.3.3.1 添加數(shù)據(jù)

SQLiteDatabase 中提供了一個 insert() 方法用于添加數(shù)據(jù)。它接收三個參數(shù),第一個參數(shù)是表名;第二個參數(shù)用于在未指定添加數(shù)據(jù)的情況下給某些可為空的列自動賦值 NULL,一般直接傳入 null 即可;第三個參數(shù)是一個 ContentValues 對象,它提供了一系列的 put() 方法重載,用于向 ContentValues 中添加數(shù)據(jù),只需要將表中的每個列名以及相應的待添加數(shù)據(jù)傳入即可。

修改 activity 中的代碼,如下所示:

public class SQLiteActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);

        . . .

        // 添加數(shù)據(jù)
        Button btn_add_data = (Button) findViewById(R.id.btn_add_data);
        btn_add_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                // 開始組裝第一條數(shù)據(jù)
                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); //插入第一條數(shù)據(jù)
                values.clear();
                // 開始組裝第二條數(shù)據(jù)
                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); //插入第二條數(shù)據(jù)
            }
        });
    }
}

運行程序,點擊添加數(shù)據(jù)按鈕,此時兩條數(shù)據(jù)應該都已經(jīng)添加成功了。為了證實一下,打開 BookStore.db 數(shù)據(jù)庫,輸入 SQL 查詢語句 “select * from Book;”,結(jié)果如下:

查看添加的數(shù)據(jù)

上圖中由于我不小心點了兩次按鈕,所以出現(xiàn)了四條數(shù)據(jù),但可以看出,剛剛組裝的兩條數(shù)據(jù),已經(jīng)添加到 book 表中了。

6.3.3.2 更新數(shù)據(jù)

SQLiteDatabase 中提供了一個 update() 方法用于更新數(shù)據(jù)。它接收四個參數(shù),第一個參數(shù)和 insert()方法一樣,是表名;第二個參數(shù)是 ContentValues 對象,要把更新數(shù)據(jù)在這里組裝進去。第三、第四個參數(shù)用于去約束更新某一行或某幾行中的數(shù)據(jù),不指定的話默認就是更新所有行。

修改 activity 中的代碼,如下所示:

public class SQLiteActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);

        . . . 

        // 更新數(shù)據(jù)
        Button btn_update_data = (Button) findViewById(R.id.btn_update_data);
        btn_update_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                // 把名字為 The Da Vinci Code 的這本書的更新成666.66
                values.put("price",666.66);
                db.update("book",values, "name = ?", new String[]{"The Da Vinci Code"});
            }
        });
    }
}

運行程序,點擊更新數(shù)據(jù)按鈕后,再次輸入查詢語句查看表中的數(shù)據(jù)情況,結(jié)果如下:

查看更新后的數(shù)據(jù)

可以看到,The Da Vinci Code 這本書的價格已經(jīng)被成功改為 666.66 了。

6.3.3.3 刪除數(shù)據(jù)

SQLiteDatabase 中提供了一個 delete()方法用于刪除數(shù)據(jù)。這個方法接收三個參數(shù),第一 個參數(shù)仍然是表名;第二、第三個參數(shù)又是用于去約束刪除某一 行或某幾行的數(shù)據(jù),不指定的話默認就是刪除所有行。

修改 activity 中的代碼,如下所示:

public class SQLiteActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);

        . . . 

        // 刪除數(shù)據(jù)
        Button btn_delete_data = (Button) findViewById(R.id.btn_delete_data);
        btn_delete_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                // 刪除頁數(shù)超過500頁的書
                db.delete("book", "pages > ?", new String[]{"500"});
            }
        });
    }
}

運行程序,點擊刪除數(shù)據(jù)按鈕后,再次輸入查詢語句查看表中的數(shù)據(jù)情況,結(jié)果如下:

查看刪除后的數(shù)據(jù)

其中 The Lost Symbol 這本書的頁數(shù)超過了 500 頁,被刪除掉了。

6.3.3.4 查詢數(shù)據(jù)

SQLiteDatabase 中提供了一個 query()方法用于查詢數(shù)據(jù)。 這個方法的參數(shù)非常復雜,最短的一個方法重載也需要傳入七個參數(shù)。

  • 第一個參數(shù)不用說,當然還是表名;
  • 第二個參數(shù)用于指定去查詢哪幾列,如果不指定則默認查詢所有列;
  • 第三、第四個參數(shù)用于去約束查詢某一行或某幾行的數(shù)據(jù),不指定則默認是查詢所有行的數(shù)據(jù);
  • 第五個參數(shù)用于指定需要去 group by 的列,不指定則表示不對查詢結(jié)果進行 group by 操作;
  • 第六個參數(shù)用于對 group by 之后的數(shù)據(jù)進行進一步的過濾,不指定則表示不進行過濾;
  • 第七個參數(shù)用 于指定查詢結(jié)果的排序方式,不指定則表示使用默認的排序方式。

更多詳細的內(nèi)容可以參考下表。其他幾個 query()方法的重載其實也大同小異。

query() 方法參數(shù)內(nèi)容

調(diào)用 query() 方法后會返回一個 Cursor 對象,查詢到的所有數(shù)據(jù)都將從這個對象中取出。

修改 activity 中的代碼,如下所示:

public class SQLiteActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sqlite);

        . . . 

        // 查詢數(shù)據(jù)
        Button btn_query_data = (Button) findViewById(R.id.btn_query_data);
        btn_query_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                // 查詢 book 表中所有數(shù)據(jù)
                Cursor cursor = db.query("book", null,null,null,null,null,null,null);
                if (cursor.moveToFirst()){
                    do {
                        // 遍歷 Cursor 對象,取出數(shù)據(jù)并打印
                        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("SQLiteActivity_query", "book name is: " + name);
                        Log.d("SQLiteActivity_query", "book author is: " + author);
                        Log.d("SQLiteActivity_query", "book pages are: " + pages);
                        Log.d("SQLiteActivity_query", "book price is: " + price);
                    }while (cursor.moveToNext());
                }
                cursor.close();
            }
        });
    }
}

運行程序,點擊查詢數(shù)據(jù)按鈕后,查看Log的打印內(nèi)容,結(jié)果如下:

打印查詢的數(shù)據(jù)

可以看到,這里已經(jīng)將 book 表中剩余兩條數(shù)據(jù)成功地讀取出來了。

6.3.4 使用 SQL 操作數(shù)據(jù)庫

下面來簡略演示一下,如何直接使用 SQL 來完成前面幾小節(jié)中學過的 CRUD 操作。

  • 添加數(shù)據(jù)的方法如下:
db.execSQL("insert into book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "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" });
  • 更新數(shù)據(jù)的方法如下:
db.execSQL("update Book set price = ? where name = ?", new String[] { "666.66", "The Da Vinci Code" });
  • 刪除數(shù)據(jù)的方法如下:
db.execSQL("delete from Book where pages > ?", new String[] { "500" });
  • 查詢數(shù)據(jù)的方法如下:
db.rawQuery("select * from Book", null);

6.4 使用 LitePal 操作數(shù)據(jù)庫

LitePal 是一款開源的 Android 數(shù)據(jù)庫框架,它采用了對象關(guān)系映射(ORM)的模式,并將我們平時開發(fā)最常用到的一些數(shù)據(jù)庫功能進行了封裝,使得不用編寫一行 SQL 語句就可以完成各種建表和增刪查改的操作。LitePal 的項目主頁上也有詳細的使用文檔,地址是:https://github.com/LitePalFramework/LitePal

6.4.1 配置 LitePal

使用 LitePal 的第一步,在 app/build.gradle 文件中引入 LitePal 的最新版本:

compile 'org.litepal.android:core:1.4.1'

項目中引入 LitePal 成功后,需要配置 litepal.xml 文件。右擊 app/src/main 目錄→New→Directory,創(chuàng)建一個 assets 目錄,然后在 assets 目錄下再新建一個 litepal.xml 文件,接著編輯 litepal.xml 文件中的內(nèi)容,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<litepal>
    <!-- 指定數(shù)據(jù)庫名 -->
    <dbname value="MyBookStore"></dbname>

    <!-- 指定數(shù)據(jù)庫版本號 -->
    <version value="1"></version>

    <!-- 指定所有的映射模型 -->
    <list>

    </list>
</litepal>

最后還要在 Application 中調(diào)用 LitePal 的初始化方法:

/**
 * 全局
 * Created by KXwon on 2016/12/9.
 */
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // 調(diào)用 LitePal 的初始化方法
        LitePal.initialize(this);
    }
}

當然別忘了在 AndroidManifest.xml 中配置 Application:

<application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    . . .
 </application>

現(xiàn)在 LitePal 的配置工作完成了,下面正式開始使用它吧。

6.4.2 創(chuàng)建和升級數(shù)據(jù)庫

介紹時說過 LitePal 采取的是對象關(guān)系映射(ORM)的模式,即把面向面向?qū)ο蟮恼Z言和面向關(guān)系的數(shù)據(jù)庫之間建立一種映射關(guān)系。對象關(guān)系映射模式可以用面向?qū)ο蟮乃季S來操作數(shù)據(jù)庫,這樣就不用和 SQL 語句打交道了。

下面用 LitePal 實現(xiàn)和上面幾小節(jié)中同樣的功能,為了方便測試,突出對比,把數(shù)據(jù)庫命名為 MyBookStore 。修改布局 activity_lite_pal.xm 中的代碼和上面例子的布局類似:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="15dp"
    android:orientation="vertical" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:text="LitePal 操作數(shù)據(jù)庫"/>

    <Button
        android:id="@+id/btn_create_database"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="創(chuàng)建數(shù)據(jù)庫"/>

    <Button
        android:id="@+id/btn_add_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="添加數(shù)據(jù)"/>

    <Button
        android:id="@+id/btn_update_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="更新數(shù)據(jù)"/>

    <Button
        android:id="@+id/btn_delete_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="刪除數(shù)據(jù)"/>

    <Button
        android:id="@+id/btn_query_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查詢數(shù)據(jù)"/>

</LinearLayout>

長這樣的:

LitePal操作數(shù)據(jù)庫布局

首先,為了創(chuàng)建一張 Book 表,先定義一個 Book 類,如下:

/**
 * Book 實體類
 * Created by KXwon on 2016/12/16.
 */

public class Book {
    private int id;
    private String author;
    private double price;
    private int pages;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

接下來將 Book 類添加到映射關(guān)系列表中,修改 litepal.xml 中的代碼如下:

<litepal>
    <!-- 指定數(shù)據(jù)庫名 -->
    <dbname value="MyBookStore"></dbname>

    <!-- 指定數(shù)據(jù)庫版本號 -->
    <version value="1"></version>

    <!-- 指定所有的映射模型 -->
    <list>
        <mapping class="com.wonderful.myfirstcode.chapter6.litepal_persistence.Book"></mapping>
    </list>
</litepal>

這里使用<mapping>標簽來聲明我們要配置的映射模型類,注意一定要使用完整的類名。不管有多少模型類型需要映射,都使用同樣的方式配置在<list>標簽下即可。

這樣就已經(jīng)把所有工作完成了,現(xiàn)只要進行任意一次數(shù)據(jù)庫的操作,MyBookStore.db 數(shù)據(jù)庫就會自動創(chuàng)建出來。修改 activity 中的代碼如下:

public class LitePalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lite_pal);

        // 創(chuàng)建數(shù)據(jù)庫
        Button btn_create_database = (Button) findViewById(R.id.btn_create_database);
        btn_create_database.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Connector.getDatabase();
            }
        });
    }
}

其中調(diào)用 Connector.getDatabase() 方法就是一次最簡單的數(shù)據(jù)庫操作。運行程序并點擊一下創(chuàng)建數(shù)據(jù)庫按鈕數(shù)據(jù)庫就自動創(chuàng)建完成了,通過adb shell 查看數(shù)據(jù)庫創(chuàng)建情況,如下:

查看數(shù)據(jù)庫文件

可以看到,數(shù)據(jù)庫文件已經(jīng)創(chuàng)建成功了(前面兩張表是上面例子創(chuàng)建的)。

接下來使用 sqlite3 命令打開 MyBookStore.db 文件,然后用 .schema 命令來查看見表語句,如下:

查看建表語句

可以看到,這有3張建表語句,其中 android_metadata 不用管,table_schema 表是 LitePal 內(nèi)部使用的,也可忽視,book 表就是根據(jù)定義的 Book 類以及類中的字段生成的。

前面使用 SQLiteOpenHelper 升級數(shù)據(jù)庫時,會把之前的表 drop 掉,造成數(shù)據(jù)丟失(當然也可已通過復雜的邏輯來避免),而使用 LitePal 這些就不再是問題了。使用 LitePal 升級數(shù)據(jù)庫很簡單,不用思考任何的邏輯,只需要改你想改的任何內(nèi)容,然后版本加1就行了。

比如想要向 Book 表中添加一個press(出版社)列,直接在 Book 類中添加一個 press 字段即可,如下所示:

public class Book {
    . . .
    private String press;

    public String getPress() {
        return press;
    }

    public void setPress(String press) {
        this.press = press;
    }
    . . .
}

與此同時,若還想在添加一張 Category 表,那么只需要新建一個 Category 類就可以了,代碼如下:

/**
 * Category 實體類
 * Created by KXwon on 2016/12/16.
 */

public class Category {
    private int id;
    private String categoryName;
    private int categoryCode;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public int getCategoryCode() {
        return categoryCode;
    }

    public void setCategoryCode(int categoryCode) {
        this.categoryCode = categoryCode;
    }
}

改完了想改的東西,修改下 litepal.xml 中的代碼如下:

<litepal>
    <!-- 指定數(shù)據(jù)庫名 -->
    <dbname value="MyBookStore"></dbname>

    <!-- 指定數(shù)據(jù)庫版本號 -->
    <version value="2"></version>

    <!-- 指定所有的映射模型 -->
    <list>
        <mapping class="com.wonderful.myfirstcode.chapter6.litepal_persistence.Book"></mapping>
        <mapping class="com.wonderful.myfirstcode.chapter6.litepal_persistence.Category"></mapping>
    </list>
</litepal>

將版本號加1,并把新的模型類添加到映射列表中。重新運行下程序,然后點擊創(chuàng)建數(shù)據(jù)庫按鈕,再查看一下新的建表語句,如下:

升級數(shù)據(jù)庫后的建表語句

這樣數(shù)據(jù)庫修改或升級就完成了,并且保留了之前表中的所有數(shù)據(jù)。

6.4.3 使用 LitePal 增刪查改

6.4.3.1 添加數(shù)據(jù)

使用 LitePal 添加數(shù)據(jù),只需要創(chuàng)建出模型類的實例,再將所有要存儲的數(shù)據(jù)設置好,最后調(diào)用一下 save() 方法就可以了。

LitePal 進行表管理操作時不需要模型類有任何繼承結(jié)構(gòu),但進行 CRUD 操作時必須繼承 DataSupport 類才行,因此需要修改 Book 類的代碼如下:

public class Book extends DataSupport{
    . . .
}

接著向 Book 表中添加數(shù)據(jù),修改 activity 中的代碼如下:

public class LitePalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lite_pal);

        . . .
        
        // 添加數(shù)據(jù)
        Button btn_add_data = (Button) findViewById(R.id.btn_add_data);
        btn_add_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 創(chuàng)建 Book 實例
                Book book = new Book();
                // 設置數(shù)據(jù)
                book.setName("第一行代碼");
                book.setAuthor("郭霖");
                book.setPages(570);
                book.setPrice(79.00);
                book.setPress("人民郵電出版社");
                // 添加數(shù)據(jù)
                book.save();
            }
        });
    }
}

現(xiàn)重新運行程序,點擊 添加數(shù)據(jù) 按鈕,此時數(shù)據(jù)就已經(jīng)添加了。同樣打開 MyBookStore.db,輸入 select*from Book;結(jié)果如下:

查看添加的數(shù)據(jù)

6.4.3.2 更新數(shù)據(jù)

使用 LitePal 更新數(shù)據(jù),它的接口比較多,這里只介紹幾種常用的。

  • 方式1
    ??對已存儲的對象重新設值,然后調(diào)用 save() 方法即可。修改 activity 中的代碼如下:
public class LitePalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lite_pal);

        . . .
        
       // 更新數(shù)據(jù)
        Button btn_update_data = (Button) findViewById(R.id.btn_update_data);
        btn_update_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Book book = new Book();
                book.setName("第一行代碼");
                book.setAuthor("郭霖");
                book.setPages(570);
                book.setPrice(79.00);
                book.setPress("人民郵電出版社");
                book.save();
                // 修改書的名稱和價格
                book.setName("第二行代碼");
                book.setPrice(88.88);
                book.save();
            }
        });
    }
}
  • 方式2
    ??對需要更新的數(shù)據(jù)進行設值,最后調(diào)用 updateAll() 方法去執(zhí)行更新操作。修改 activity 中的代碼如下:
public class LitePalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lite_pal);

        . . .
        
        // 更新數(shù)據(jù)
        Button btn_update_data = (Button) findViewById(R.id.btn_update_data);
        btn_update_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Book book = new Book();
                book.setName("第二行代碼");
                book.setPrice(88.88);
                book.updateAll("name = ? and author = ?","第一行代碼","郭霖");
            }
        });
    }
}

現(xiàn)重新運行程序,點擊 更新數(shù)據(jù) 按鈕,查詢表中的數(shù)據(jù)情況,結(jié)果如下:

查看更新后的數(shù)據(jù)

注意事項:在使用 updateAll() 方法時,若你把一個字段的值更新為默認值時,是不可以使用上面的方式來 set 數(shù)據(jù)的。對于想要將數(shù)據(jù)更新為默認值,LitePal 統(tǒng)一提供了一個 setToDefault() 方法,然后傳入相應的列名就可以了。如將所有書的頁數(shù)都更新為默認值 0,直接調(diào)用 book.setPages(0) 是不可以的,但可以這樣寫:

Book book = new Book();
book.setToDefault("pages");
book.updateAll();

6.4.3.3 刪除數(shù)據(jù)

使用 LitePal 刪除數(shù)據(jù)的方式主要有兩種:第一種就是直接調(diào)用已存儲的對象的 delete() 方法就可以了;第二種是調(diào)用 DataSupport.deleteAll() 方法來刪除。

這里演示下第二種方式,修改 activity 中的代碼如下:

public class LitePalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lite_pal);

        . . .
        
        // 刪除數(shù)據(jù)
        Button btn_delete_data = (Button) findViewById(R.id.btn_delete_data);
        btn_delete_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 刪除 Book 表中價格低于15的書
                DataSupport.deleteAll(Book.class, "price < ?", "15");
            }
        });
    }
}

另外,deleteAll() 方法若不指定約束條件,就意味著刪除表中的所有數(shù)據(jù)。

6.4.3.4 查詢數(shù)據(jù)

使用 LitePal 查詢數(shù)據(jù)一點都不復雜。

比如想要實現(xiàn)上節(jié)中的查詢 Book 表,修改 activity 的代碼如下:

public class LitePalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lite_pal);

        . . .
        
        // 查詢數(shù)據(jù)
        Button btn_query_data = (Button) findViewById(R.id.btn_query_data);
        btn_query_data.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 查詢 Book 表
                List<Book> books = DataSupport.findAll(Book.class);
                // 打印 Book 表中的信息
                for (Book book: books){
                    Log.d("LitePalActivity_query", "book name is: " + book.getName());
                    Log.d("LitePalActivity_query", "book author is: " + book.getAuthor());
                    Log.d("LitePalActivity_query", "book pages are: " + book.getPages());
                    Log.d("LitePalActivity_query", "book price is: " + book.getPrice());
                    Log.d("LitePalActivity_query", "book press is: " + book.getPress());
                }
            }
        });
    }
}

沒錯,就是這么簡單。現(xiàn)重新運行程序,點擊 查詢數(shù)據(jù) 按鈕,查看Log信息如下:

打印查詢的數(shù)據(jù)

除了 findAll() 方法之外,LitePal 還提供了其他非常有用的 API。比如:

  • 查詢 Book 表中的第一條數(shù)據(jù):
Book firstBook = DataSupport.findFirst(Book.class);
  • 查詢 Book 表中的最后一條數(shù)據(jù):
Book LastBook = DataSupport.findLast(Book.class);

還可以通過連綴查詢來制定更多的查詢功能:

  • select() 方法用于指定查詢哪幾列的數(shù)據(jù)。
    如查詢 name 和 author 這兩列數(shù)據(jù):
List<Book> books = DataSupport.select("name","author").find(Book.class);
  • where() 方法用于指定查詢的約束條件。
    如查詢頁數(shù)大450的數(shù)據(jù):
List<Book> books = DataSupport.where("pages > ?","450").find(Book.class);
  • order() 方法用于指定結(jié)果的排序方式。
    如查詢結(jié)果按書價從高到低排列:
// 其中 desc 表示降序,asc 或者不寫表示升序
List<Book> books = DataSupport.order("price desc").find(Book.class);
  • limit() 方法用于指定查詢結(jié)果的數(shù)量。
    如查詢表中前3條數(shù)據(jù):
List<Book> books = DataSupport.limit(3).find(Book.class);
  • offset() 方法用于指定查詢結(jié)果的偏移量。
    如查詢表中的第2、3、4條數(shù)據(jù):
List<Book> books = DataSupport.limit(3).offset(1).find(Book.class);

當然你也可以對這5個方法進行任意的連綴組合,如:

    // 查詢 Book 表中第11-20跳滿足頁數(shù)大于450這個條件的 name、author 和 pages 這3列數(shù)據(jù),
    // 并將查詢結(jié)果按照頁數(shù)升序排序
    List<Book> books = DataSupport.select("name","author","pages")
            .where("pages > ?","450")
            .order("pages")
            .limit(10)
            .offset(10)
            .find(Book.class);

LitePal 也可以支持使用原生的 SQL 語句來進行查詢:

Cursor c = DataSupport.findBySQL("select * from Book where pages > ? and price < ?","450","20");

關(guān)于 LitePal 就介紹到這。

有木有感覺 LitePal 很強大?

哎呦我去,這章終于結(jié)束了,雖然不難,但介紹起來真繁瑣。明天又到周末了,可以愉快得玩耍了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • 第 6 章 數(shù)據(jù)存儲全方案,詳解持久化技術(shù) 一:文件存儲 將數(shù)據(jù)存儲到文件中(使用 Java 流的方式將數(shù)據(jù)寫入到...
    jesse0閱讀 476評論 0 2
  • 圖為文章結(jié)構(gòu)思維導圖 生活不是用來妥協(xié)的,明白要趁早,否則就一切成為零。 一個人越善良,帶人底線應該...
    kly在路上閱讀 2,529評論 6 49

友情鏈接更多精彩內(nèi)容