1. 程式人生 > >一篇好文之Android資料庫GreenDao的完全解析

一篇好文之Android資料庫GreenDao的完全解析

640?wx_fmt=png


今日科技快訊


小米近日稱,財政部此次公告的檢查為2017年財政部會計監督檢查,是針對2016年的會計資訊質量進行的檢查。公司存在部分費用攤銷核算錯誤、對外贈送商品未作為視同銷售行為申報繳稅、報銷發票管理不規範、費用管理制度不完善等問題。以上問題均已整改完成,並獲得財政部認可。


作者簡介


本篇轉自 aserbao 的部落格,給大家帶來了 GreenDao 的完全解析,希望對大家有所幫助。

aserbao 的部落格地址:

https://me.csdn.net/qq_32175491


前言


之前在開發過程中,資料庫基本上會使用 Litepal 和 SQlite 自己寫,最近換新環境,公司原先使用的資料庫就是 GreenDao,在各種情況的作用下,準備瞭解下 GreenDao,順便寫一篇文章記錄下 GreenDao 的基本使用!大家在使用 GreenDao 的時候遇到什麼問題,歡迎幫忙給我留言。

迴歸正題,不再扯沒用的了!本文主要從如下幾個方面進行講解:

1. 儲存的資料庫結構 

2. GreenDao的優缺點 

3. GreenDao 的使用配置 

4. 使用GreenDao實現資料的增刪改查 

5. GreenDao的註解使用

 6. GreenDao的關係處理

 7. GreenDao資料庫加密

咱們先看一波最終的效果圖:

640?wx_fmt=gif


儲存的資料庫結構


學習資料庫之前,我們先得設計自己的資料庫,不多廢話,下面是我此次學習的資料庫結構,後面所有的資料請參考這個圖進行學習:

640?wx_fmt=png


GreenDao的介紹


什麼是GreenDao?

GreenDAO 是一個開源的 Android ORM(“物件/關係對映”),通過 ORM(稱為“物件/關係對映”),在我們資料庫開發過程中節省了開發時間!

640?wx_fmt=png

GreenDao的官方文件

GreenDao,適用於您的SQLite資料庫的 Android ORM:

http://greenrobot.org/greendao/

GreenDao的作用?

通過 GreenDao,我們可以更快速的操作資料庫,我們可以使用簡單的面相物件的API來儲存,更新,刪除和查詢 Java 物件。

GreenDao的優缺點?

  1. 高效能,下面是官方給出的關於 GreenDao,OrmLite 和 ActiveAndroid 三種 ORM 解決方案的資料統計圖:

    640?wx_fmt=png

  2. 易於使用的強大 API,涵蓋關係和連線;

  3. 最小的記憶體消耗;

  4. 小庫大小(<100KB)以保持較低的構建時間並避免65k 方法限制;

  5. 資料庫加密:greenDAO 支援 SQLCipher,以確保使用者的資料安全;


GreenDao的使用


GreenDao 的核心類有三個:分別是 DaoMaster,DaoSession,XXXDao,這三個類都會自動建立,無需自己編寫建立!

  • DaoMaster::DaoMaster 儲存資料庫物件(SQLiteDatabase)並管理特定模式的 DAO 類(而不是物件)。它有靜態方法來建立表或刪除它們。它的內部類 OpenHelper 和DevOpenHelper 是 SQLiteOpenHelper 實現,它們在 SQLite 資料庫中建立模式。

  • DaoSession:管理特定模式的所有可用 DAO 物件,您可以使用其中一個getter方法獲取該物件。DaoSession 還提供了一些通用的永續性方法,如實體的插入,載入,更新,重新整理和刪除。

  • XXXDao:資料訪問物件(DAO)持久存在並查詢實體。對於每個實體,greenDAO 生成DAO。它具有比 DaoSession 更多的永續性方法,例如:count,loadAll 和 insertInTx。

  • Entities :可持久化物件。通常, 實體物件代表一個數據庫行使用標準 Java 屬性(如一個POJO 或 JavaBean )。

    

640?wx_fmt=png

匯入 Gradle 外掛和 Dao 程式碼生成

要在 Android 專案中使用 GreenDao,您需要新增 GreenDao Gradle 外掛並新增GreenDao 庫:

匯入外掛

// 在 Project的build.gradle 檔案中新增:
buildscript {
    repositories {
        jcenter()
        mavenCentral() // add repository
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin
    }
}

配置相關依賴

// 在 Moudle:app的  build.gradle 檔案中新增:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin

dependencies {
    implementation 'org.greenrobot:greendao:3.2.2' // add library
}

配置資料庫相關資訊

greendao {
    schemaVersion 1 //資料庫版本號
    daoPackage 'com.aserbao.aserbaosandroid.functions.database.greenDao.db'
// 設定DaoMaster、DaoSession、Dao 包名
    targetGenDir 'src/main/java'//設定DaoMaster、DaoSession、Dao目錄
    generateTests false //設定為true以自動生成單元測試。
    targetGenDirTests 'src/main/java' //應儲存生成的單元測試的基本目錄。預設為 src / androidTest / java。
}

GreenDao初始化

我們可以在Application中維持一個全域性的會話。我們在 Applicaiton 進行資料庫的初始化操作:

 /**
     * 初始化GreenDao,直接在Application中進行初始化操作
     */

    private void initGreenDao() {
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this"aserbao.db");
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
    }

    private DaoSession daoSession;
    public DaoSession getDaoSession() {
        return daoSession;
    }

建立儲存物件實體類

使用 GreenDao 儲存資料只需要在儲存資料類前面宣告 @Entity 註解就讓 GreenDao 為其生成必要的程式碼:

@Entity
public class Student {
    @Id(autoincrement = true)
    Long id;
    @Unique
    int studentNo;//學號
    int age; //年齡
    String telPhone;//手機號
    String sex; //性別
    String name;//姓名
    String address;//家庭住址
    String schoolName;//學校名字
    String grade;//幾年級
    ……getter and setter and constructor method……
    }

這時候重新 build 一下專案會發現在設定的 targetGenDir 的目錄生成三個類檔案,這個是GreenDao 自動生成的!

使用 GreenDao 實現增刪改查

insert() 插入資料

@Override
    public void insertData(Thing s) {
     DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
            for (int i = 0; i < 1000; i++) {
               Student student = new Student();
                        student.setStudentNo(i);
                        int age = mRandom.nextInt(10) + 10;
                        student.setAge(age);
                        student.setTelPhone(RandomValue.getTel());
                        String chineseName = RandomValue.getChineseName();
                        student.setName(chineseName);
                        if (i % 2 == 0) {
                            student.setSex("男");
                        } else {
                            student.setSex("女");
                        }
                        student.setAddress(RandomValue.getRoad());
                        student.setGrade(String.valueOf(age % 10) + "年紀");
                        student.setSchoolName(RandomValue.getSchoolName());
                        daoSession.insert(student);
             }
    }

insertOrReplace()**資料存在則替換,資料不存在則插入

@Override
    public void insertData(Thing s) {
    DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
            for (int i = 0; i < 1000; i++) {
                 Student student = new Student();
                        student.setStudentNo(i);
                        int age = mRandom.nextInt(10) + 10;
                        student.setAge(age);
                        student.setTelPhone(RandomValue.getTel());
                        String chineseName = RandomValue.getChineseName();
                        student.setName(chineseName);
                        if (i % 2 == 0) {
                            student.setSex("男");
                        } else {
                            student.setSex("女");
                        }
                        student.setAddress(RandomValue.getRoad());
                        student.setGrade(String.valueOf(age % 10) + "年紀");
                        student.setSchoolName(RandomValue.getSchoolName());
                        daoSession.insertOrReplace(student);//插入或替換
             }
    }

刪除有兩種方式:delete()和 deleteAll();分別表示刪除單個和刪除所有。

@Override
    public void deleteData(Student s) {
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        daoSession.delete(s);
    }

@Override
    public void deleteAll() {
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        daoSession.deleteAll(Student.class);
    }

通過 update 來進行修改:

@Override
    public void updataData(Student s) {
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        daoSession.update(s);
    }

查詢的方法有:

  • loadAll():查詢所有資料。

  • queryRaw():根據條件查詢。

  • queryBuilder() : 方便查詢的建立,後面詳細講解。

public List queryAll(){
        List<Student> students = daoSession.loadAll(Student.class);
        return students;
    }
@Override
    public void queryData(String s) {
       List<Student> students = daoSession.queryRaw(Student.class, " where id = ?", s);
        mDataBaseAdapter.addNewStudentData(students);
    }


QueryBuilder的使用


編寫 SQL 可能很困難並且容易出現錯誤,這些錯誤僅在執行時才會被注意到。該QueryBuilder 的類可以讓你建立你的實體,而不 SQL 自定義查詢,並有助於在編譯時已檢測錯誤。

我們先講下 QueryBuilder 的常見方法:

  • where(WhereCondition cond, WhereCondition… condMore): 查詢條件,引數為查詢的條件!

  • or(WhereCondition cond1, WhereCondition cond2, WhereCondition… condMore): 巢狀條件或者,用法同 or。

  • and(WhereCondition cond1, WhereCondition cond2, WhereCondition… condMore): 巢狀條件且,用法同 and。

  • join(Property sourceProperty, Class destinationEntityClass):多表查詢,後面會講。

  • 輸出結果有四種方式,選擇其中一種最適合的即可,list()返回值是 List,而其他三種返回值均實現 Closeable,需要注意的不使用資料時遊標的關閉操作:

  • list ()所有實體都載入到記憶體中。結果通常是一個沒有魔法的 ArrayList。最容易使用。

  • listLazy ()實體按需載入到記憶體中。首次訪問列表中的元素後,將載入並快取該元素以供將來使用。必須關閉。

  • listLazyUncached ()實體的“虛擬”列表:對列表元素的任何訪問都會導致從資料庫載入其資料。必須關閉。

  • listIterator ()讓我們通過按需載入資料(懶惰)來迭代結果。資料未快取。必須關閉。

GreenDao 中 SQL 語句的縮寫,我們也瞭解下,原始碼在Property中,使用的時候可以自己點進去查詢即可:

  • eq():“equal (’=?’)” 等於;

  • notEq() :“not equal (’<>?’)” 不等於;

  • like():" LIKE ?" 值等於;

  • between():" BETWEEN ? AND ?" 取中間範圍;

  • in():" IN (" in命令;

  • notIn():" NOT IN (" not in 命令;

  • gt():">?" 大於;

  • lt():"<? " 小於;

  • ge():">=?" 大於等於;

  • le():"<=? " 小於等於;

  • isNull():" IS NULL" 為空;

  • isNotNull():" IS NOT NULL" 不為空;

使用QueryBuilder進行查詢操作

  • 簡單條件查詢

查詢當前Student表的所有的資料:

 public List queryAllList(){
          DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class);
        List<Student> list = qb.list(); // 查出所有的資料
    return list;
    }

查詢 Name 為“一”的所有 Student:

public List queryListByMessage(String name){
         DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class);
        QueryBuilder<Student> studentQueryBuilder = qb.where(StudentDao.Properties.Name.eq("一")).orderAsc(StudentDao.Properties.Name);
        List<Student> studentList = studentQueryBuilder.list(); //查出當前對應的資料
        return list;
    }
  • 原始查詢

通過原始的 SQL 查詢語句進行查詢!其實上面有提到 QueryBuilder 的目的就是方便快捷的編寫 SQL 查詢語句,避免我們自己在編寫過程中出錯!簡單介紹下通過 QueryBuilder 編寫資料庫,方式方法如下 :

public List queryListBySqL(){
// 查詢ID大於5的所有學生
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        Query<Student> query = daoSession.queryBuilder(Student.class).where(
                new WhereCondition.StringCondition("_ID IN " +
                        "(SELECT _ID FROM STUDENT WHERE _ID > 5)")
        ).build();
        List<Student> list = query.list();
        return list;
    }
  • 巢狀條件查詢

查詢 Id 大於5小於10,且 Name 值為"一"的資料:

public List queryList(){
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class);
        qb = daoSession.queryBuilder(Student.class);
        List<Student> list2 = qb.where(StudentDao.Properties.Name.eq("一"),
                qb.and(StudentDao.Properties.Id.gt(5),
                        StudentDao.Properties.Id.le(50))).list();
        return  list2;
    }

取10條 Id 大於1的資料,且偏移2條

 public List queryListByOther(){
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class);

        //搜尋條件為Id值大於1,即結果為[2,3,4,5,6,7,8,9,10,11];
        // offset(2)表示往後偏移2個,結果為[4,5,6,7,8,9,10,11,12,13];
        List<Student> list = qb.where(StudentDao.Properties.Id.gt(1)).limit(10).offset(2).list();
        return list;
    }
  • 多次執行查詢

使用 QueryBuilder 構建查詢後,可以重用 Query 物件以便稍後執行查詢。這比始終建立新的 Query 物件更有效。如果查詢引數沒有更改,您可以再次呼叫 list / unique 方法。可以通過 setParameter 方法來修改條件引數值:

public List queryListByMoreTime(){
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class);

        //搜尋條件為Id值大於1,即結果為[2,3,4,5,6,7,8,9,10,11];
        // offset(2)表示往後偏移2個,結果為[4,5,6,7,8,9,10,11,12,13];
        Query<Student> query = qb.where(StudentDao.Properties.Id.gt(1)).limit(10).offset(2).build();
        List<Student> list = query.list();

        //通過SetParameter來修改上面的查詢條件,比如我們將上面條件修改取10條Id值大於5,往後偏移兩位的資料,方法如下!
        query.setParameter(0,5);
        List<Student> list1 = query.list();
        return list1;
    }
  • 在多個執行緒中使用 QueryBuilder

如果在多個執行緒中使用查詢,則必須呼叫 forCurrentThread ()以獲取當前執行緒的 Query例項。Query 的物件例項繫結到構建查詢的擁有執行緒。

這使您可以安全地在 Query 物件上設定引數,而其他執行緒不會干擾。如果其他執行緒嘗試在查詢上設定引數或執行繫結到另一個執行緒的查詢,則會丟擲異常。像這樣,您不需要同步語句。實際上,您應該避免鎖定,因為如果併發事務使用相同的 Query 物件,這可能會導致死鎖。

每次呼叫 forCurrentThread ()時, 引數都會在使用其構建器構建查詢時設定為初始引數。

使用QueryBuilder進行批量刪除操作

使用 QueryBuilder 進行批量刪除操作,不會刪除單個實體,但會刪除符合某些條件的所有實體。要執行批量刪除,請建立 QueryBuilder,呼叫其 buildDelete ()方法,然後執行返回的 DeleteQuery。

例子:刪除資料庫中id大於5的所有其他資料

public boolean deleteItem(){
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> where = daoSession.queryBuilder(Student.class).where(StudentDao.Properties.Id.gt(5));
        DeleteQuery<Student> deleteQuery = where.buildDelete();
        deleteQuery.executeDeleteWithoutDetachingEntities();
        return false;
    }


註解講解


從 GreenDao 3 使用註解來定義模型和實體,前面也講過,通過註解的使用可以快速構建資料庫表,包括設定主鍵,自增,值是否唯一等等等……

下面我們來看下註解的簡單使用:

@Entity
public class Student {
    @Id(autoincrement = true)
    Long id;
    @Unique
    int studentNo;//學號
    int age; //年齡
    String telPhone;//手機號
    String sex; //性別
    String name;//姓名
    Str