1. 程式人生 > >Android 資料庫ORM開源框架之greenDAO

Android 資料庫ORM開源框架之greenDAO

我相信,在平時的開發過程中,大家一定會或多或少地接觸到 SQLite。然而在使用它時,我們往往需要做許多額外的工作,像編寫 SQL 語句與解析查詢結果等。所以,適用於 Android 的ORM 框架也就孕育而生了,現在市面上主流的框架有 OrmLite、SugarORM、Active Android、Realm 與 GreenDAO。而今天的主角便是 greenDAO,下面,我將詳解地介紹如何在 Android Studio 上使用 greenDAO,並結合程式碼總結一些使用過程中的心得。
這裡寫圖片描述
簡單的講,greenDAO 是一個將物件對映到 SQLite 資料庫中的輕量且快速的 ORM 解決方案。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.)
而關於 ORM (Object Relation Mapping - 物件關係對映)的概念,可參見 Wikipedia。

GREENDAO 設計的主要目標
1.一個精簡的庫

2.效能最大化

3.記憶體開銷最小化

4.易於使用的 APIs

5.對 Android 進行高度優化

GREENDAO 設計的主要特點
1.greenDAO 效能遠遠高於同類的 ORMLite,具體測試結果可見官網

2.greenDAO 支援 protocol buffer(protobuf) 協議資料的直接儲存,如果你通過 protobuf 協議與伺服器互動,將不需要任何的對映。

3.與 ORMLite 等使用註解方式的 ORM 框架不同,greenDAO 使用「Code generation」的方式,這也是其效能能大幅提升的原因。

為了在我們的 Android 工程中使用 greenDAO ,我們需要另建一個純 Java Project,用於自動生成後繼 Android 工程中需要使用到的 Bean、DAO、DaoMaster、DaoSession 等類。

關於以上幾個類的相關概念與作用,我將在下面的程式碼(註釋)中詳細講解。
當然,你也可以在 官網 中找到相關介紹。

開始==================
一. 新建一個android專案,greenDAO 生成的 Bean、DAO、DaoMaster、DaoSession 等類後面會存在這個android專案中。
二. 新建「GREENDAO GENERATOR」模組 (純 JAVA 工程)
1.通過 File -> New -> New Module -> Java Library -> 填寫相應的包名與類名 -> Finish.
這裡寫圖片描述


這裡寫圖片描述
2.編寫 ExampleDaoGenerator 類,注意: 我們的 Java 工程只有一個類,它的內容決定了「GreenDao Generator」的輸出,你可以在這個類中通過物件、關係等建立資料庫結構,下面我將以註釋的形式詳細講解程式碼內容。

package com.example;

import de.greenrobot.daogenerator.DaoGenerator;
import de.greenrobot.daogenerator.Entity;
import de.greenrobot.daogenerator.Schema;

public class ExampleDaoGenerator {
    public static void main(String[] args) throws Exception {
        // 正如你所見的,你建立了一個用於新增實體(Entity)的模式(Schema)物件。
        // 兩個引數分別代表:資料庫版本號與自動生成程式碼的包路徑。
        Schema schema = new Schema(1, "teach.focus.testgreendao");
//      當然,如果你願意,你也可以分別指定生成的 Bean 與 DAO 類所在的目錄,只要如下所示:
//      Schema schema = new Schema(1, "me.itangqi.bean");
//      schema.setDefaultJavaPackageDao("me.itangqi.dao");

        // 模式(Schema)同時也擁有兩個預設的 flags,分別用來標示 entity 是否是 activie 以及是否使用 keep sections。
        // schema2.enableActiveEntitiesByDefault();
        // schema2.enableKeepSectionsByDefault();

        // 一旦你擁有了一個 Schema 物件後,你便可以使用它新增實體(Entities)了。
        addNote(schema);
//        addNotes(schema);
        // 最後我們將使用 DAOGenerator 類的 generateAll() 方法自動生成程式碼,此處你需要根據自己的情況更改輸出目錄,目前會在android專案中src/main/java/teach.focus.testgreendao下面生成。
        // 其實,輸出目錄的路徑可以在 build.gradle 中設定,有興趣的朋友可以自行搜尋,這裡就不再詳解。
        new DaoGenerator().generateAll(schema, "app/src/main/java");
    }

    /**
     * @param schema
     */
    private static void addNote(Schema schema) {
        // 一個實體(類)就關聯到資料庫中的一張表,此處表名為「Note」(既類名)
        Entity note = schema.addEntity("Note");
        // 你也可以重新給表命名
        // note.setTableName("NODE");

        // greenDAO 會自動根據實體類的屬性值來建立表字段,並賦予預設值
        // 接下來你便可以設定表中的欄位:
        note.addIdProperty();
        note.addStringProperty("text").notNull();
        // 與在 Java 中使用駝峰命名法不同,預設資料庫中的命名是使用大寫和下劃線來分割單詞的。
        // For example, a property called “creationDate” will become a database column “CREATION_DATE”.
        note.addStringProperty("comment");
        note.addDateProperty("date");
    }
    /**
     * @param schema
     */
    private static void addNotes(Schema schema) {
        // 一個實體(類)就關聯到資料庫中的一張表,此處表名為「Note」(既類名)
        Entity note = schema.addEntity("Notes");
        // 你也可以重新給表命名
        // note.setTableName("NODE");

        // greenDAO 會自動根據實體類的屬性值來建立表字段,並賦予預設值
        // 接下來你便可以設定表中的欄位:
        note.addIdProperty();
        note.addStringProperty("text").notNull();
        // 與在 Java 中使用駝峰命名法不同,預設資料庫中的命名是使用大寫和下劃線來分割單詞的。
        // For example, a property called “creationDate” will become a database column “CREATION_DATE”.
        note.addStringProperty("comment");
        note.addDateProperty("date");
    }
}

三. 生成 DAO 檔案(資料庫)
執行 generator 工程,如一切正常,你將會在控制檯看到如下日誌,並且在主工程「java-gen」下會發現生成了DaoMaster、DaoSession、NoteDao、Note共4個類檔案。
這裡寫圖片描述
如果在此處出錯,你可以依據錯誤日誌進行排查,主要看是否輸出目錄存在?其他配置是否正確?等
四. 在 ANDROID 工程中進行資料庫操作
NoteActivity.java

public class NoteActivity extends ListActivity {
    private SQLiteDatabase db;
    private EditText editText;
    private DaoMaster daoMaster;
    private DaoSession daoSession;
    private Cursor cursor;
    public static final String TAG = "DaoExample";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_note);
        // 官方推薦將獲取 DaoMaster 物件的方法放到 Application 層,這樣將避免多次建立生成 Session 物件
        setupDatabase();
        // 獲取 NoteDao 物件
        getNoteDao();

        String textColumn = NoteDao.Properties.Text.columnName;
        String orderBy = textColumn + " COLLATE LOCALIZED ASC";
        cursor = db.query(getNoteDao().getTablename(), getNoteDao().getAllColumns(), null, null, null, null, orderBy);
        String[] from = {textColumn, NoteDao.Properties.Comment.columnName};
        int[] to = {android.R.id.text1, android.R.id.text2};

        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursor, from,
                to);
        setListAdapter(adapter);

        editText = (EditText) findViewById(R.id.editTextNote);
    }

    private void setupDatabase() {
        // 通過 DaoMaster 的內部類 DevOpenHelper,你可以得到一個便利的 SQLiteOpenHelper 物件。
        // 可能你已經注意到了,你並不需要去編寫「CREATE TABLE」這樣的 SQL 語句,因為 greenDAO 已經幫你做了。
        // 注意:預設的 DaoMaster.DevOpenHelper 會在資料庫升級時,刪除所有的表,意味著這將導致資料的丟失。
        // 所以,在正式的專案中,你還應該做一層封裝,來實現資料庫的安全升級。
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
        db = helper.getWritableDatabase();
        // 注意:該資料庫連線屬於 DaoMaster,所以多個 Session 指的是相同的資料庫連線。
        daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
    }

    private NoteDao getNoteDao() {
        return daoSession.getNoteDao();
    }

    /**
     * Button 點選的監聽事件
     *
     * @param view
     */
    public void onMyButtonClick(View view) {
        switch (view.getId()) {
            case R.id.buttonAdd:
                addNote();
                break;
            case R.id.buttonSearch:
                search();
                break;
            default:
                Log.d(TAG, "what has gone wrong ?");
                break;
        }
    }

    private void addNote() {
        String noteText = editText.getText().toString();
        editText.setText("");

        final DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
        String comment = "Added on " + df.format(new Date());

        // 插入操作,簡單到只要你建立一個 Java 物件
        Note note = new Note(null, noteText, comment, new Date());
        getNoteDao().insert(note);
        Log.d(TAG, "Inserted new note, ID: " + note.getId());
        cursor.requery();
    }

    private void search() {
        // Query 類代表了一個可以被重複執行的查詢
        Query query = getNoteDao().queryBuilder()
                .where(NoteDao.Properties.Text.eq("Test1"))
                .orderAsc(NoteDao.Properties.Date)
                .build();

//      查詢結果以 List 返回
//      List notes = query.list();
        // 在 QueryBuilder 類中內建兩個 Flag 用於方便輸出執行的 SQL 語句與傳遞引數的值
        QueryBuilder.LOG_SQL = true;
        QueryBuilder.LOG_VALUES = true;
    }

    /**
     * ListView 的監聽事件,用於刪除一個 Item
     * @param l
     * @param v
     * @param position
     * @param id
     */
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        // 刪除操作,你可以通過「id」也可以一次性刪除所有
        getNoteDao().deleteByKey(id);
//        getNoteDao().deleteAll();
        Log.d(TAG, "Deleted note, ID: " + id);
        cursor.requery();
    }
}