GreenDAO3.0應用指南

GreenDAO簡介

在Android項目開發(fā)過程中弯淘,sqlite來保存數據禾蚕。由于原生api用起來比較麻煩,所以現在興起很多的開源ORM框架勉失。所以總的來說GreenDAO就是一款非常高效易用的ORM框架GitHub地址。相比于其他框架主要有以下特點:

  • 性能高效
  • 簡潔的API
  • 為Android做了很多優(yōu)化
  • 內存消耗小
    網上有哥們兒專門做了性能對比實驗皂林,大家可以看看(http://www.tuicool.com/articles/7VVfye
    其實GreenDAO已經出來很久朗鸠,但是之前的版本用起來真的非常麻煩。項目中集成起來不方便础倍,而且學習成本相對較高烛占。但是前端時間升級到3.0之后,用起來可以說得上是“傻瓜式”沟启,下面就讓我們開始玩玩GreenDAO3.0忆家。

集成GreenDAO

首先需要在project的gradle文件中引入greenDAO插件:

project的gradle

然后在module的gradle文件中添加greenDAO的插件,并引入相關類庫:

module的gradle

Gradle 插件配置:

  • schemaVersion: 數據庫schema版本德迹,也可以理解為數據庫版本號芽卿;
  • daoPackage:設置DaoMaster 、DaoSession胳搞、Dao包名卸例;
  • targetGenDir:設置DaoMaster 、DaoSession肌毅、Dao目錄筷转;
  • targetGenDirTest:設置生成單元測試目錄;
  • generateTests:設置自動生成單元測試用例悬而。

在這里需要注意首先引入的是引入相關插件呜舒,然后在dependencies中引入greenDAO的類庫。其中schemaVersion表示數據庫版本號笨奠,每次數據庫升級的時候我們修改這里的版本號即可(修改這里的版本號袭蝗,greenDAO會自動修改生成到DAOMaster中的版本號),targetGenDir表示greenDAO生成的DAOMaster和DaoSession的位置艰躺。做完這一切我們就已經成功將greenDAO引入到我們的項目中了呻袭。


使用GreenDAO

數據庫引入成功后眨八,在使用之前腺兴,我們還得先來創(chuàng)建一個實體類:

編寫實體類

@Entity表示這個實體類一會會在數據庫中生成對應的表,@Id表示該字段是id廉侧,小伙伴們注意該字段的數據類型為包裝類型Long页响,為什么要用包裝類型呢?我們一會插入數據的時候再說段誊。@Property則表示該屬性將作為表的一個字段闰蚕,其中nameInDb看名字就知道這個屬性在數據庫中對應的數據名稱。OK连舍,寫完這些之后將項目進行編譯没陡,編譯成功之后系統(tǒng)會幫助我們生成相應的構造方法和get/set方法,并且還會在我們的包下生成DaoMaster和DaoSession。

Paste_Image.png

接下來我們就可以進行數據庫的初始化了(官方推薦在Application下面做):


public class MyApplication extends Application {
    private DaoSession daoSession;

    @Override
    public void onCreate() {
        super.onCreate();

        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this,"test.db",null);
        Database db = helper.getWritableDb();
        daoSession = new DaoMaster(db).newSession();
    }

    public DaoSession getDaoSession() {
        return daoSession;
    }
}

首先獲取一個DevOpenHelper對象盼玄,這個類有點類似于我們使用的SqliteOpenHelper贴彼,我們主要在這個類中對數據庫的版本進行管理。同時我們在這里提供一個借口用來提供DaoSession對象埃儿。因為我們需要用DAOSession來獲取DAO對象器仗,然后使用DAO對象操作數據庫。


數據庫操作CRUD

數據庫的增刪改查我們都將通過UserDao來進行童番,所以我們需要先對UserDao進行一個初始化:

        daoSessin = ((MyApplication) getApplication()).getDaoSession();
        userDao = daoSessin.getUserDao();

添加數據

        User addUser = new User();
        addUser.setUsername("xiaoming");
        addUser.setNickname("angle");
        userDao.insert(addUser);

修改數據

修改數據需要先執(zhí)行查詢精钮,將待修改的數據查詢出來,然后再分別執(zhí)行修改:

        List<User> updateList = userDao.queryBuilder().where(UserDao.Properties.Username.eq("tianrenzheng")).build().list();
        for (User u : updateList) {
            u.setNickname("updated angle");
            userDao.update(u);
        }

刪除數據

刪除數據原理同修改數據類似:

        List<User> deleteList = userDao.queryBuilder().where(UserDao.Properties.Username.eq("tianrenzheng")).build().list();
        for (User u : deleteList) {
            userDao.delete(u);
        }

查詢數據

由上面的這個操作剃斧,可以看出查詢操作在GreenDAO使用過程中的重要性轨香,因為修改和刪除數據都需要先將數據查詢出來才能操作。所以理解和熟悉查詢語句是GreenDAO日常使用中重點:

        Query<User> userQuery = userDao.queryBuilder().orderAsc(UserDao.Properties.Id).build();
        List<User> users = userQuery.list();
        for (User u : users) {
            Log.d(TAG, u.toString());
        }

這里只是一個簡單的例子悯衬,SQL語句的所有操作弹沽,我們可以通過構建Query對象來實現策橘。這里大家在實踐中去摸索买决,函數的命名跟SQL關鍵字都是類似的。


數據庫升級

數據庫的升級其實就兩個步驟我們來看看:

1.修改gradle文件

首先在module的gradle文件中修改版本號:

Paste_Image.png

2.修改實體類

@Entity  
public class User {  
    @Property  
    private int age;  
    @Property  
    private String password;  
    @Id  
    private Long id;  
    @Property(nameInDb = "USERNAME")  
    private String username;  
    @Property(nameInDb = "NICKNAME")  
    private String nickname;  
}  

重新編譯項目運行即可没卸。一般的數據庫升級這樣就可以了,特殊情況可能需要自己編寫數據庫遷移腳本细卧,這種時候可以自定義DBHelper赋铝,定義方式如下,注意繼承類:

public class DBHelper extends DaoMaster.OpenHelper {  
    public static final String DBNAME = "lenve.db";  
  
    public DBHelper(Context context) {  
        super(context, DBNAME, null);  
    }  
  
    @Override  
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
        super.onUpgrade(db, oldVersion, newVersion);  
    }  
}  

可以在onUpgrade方法中進行數據庫的遷移筑凫,如果自定義了DBHelper,則數據庫的初始化變?yōu)槿缦路绞剑?/p>

DBHelper devOpenHelper = new DBHelper(this);  
DaoMaster daoMaster = new DaoMaster(devOpenHelper.getWritableDb());  
DaoSession daoSession = daoMaster.newSession();  
userDao = daoSession.getUserDao();  

多表關聯(lián)

數據庫的表可能涉及到一對一和一對多關系丸边,在greenDAO中涉及到to-one和to-many關系骄呼。例如哆致,你想在greenDAO中建立一個對多關系模型臣咖,你就 需要使用to-one和to-many關系。但是夺蛇,請注意疚漆,to-one和to-many關系是不相關的,所以你必須同時更新刁赦。

1:1關聯(lián)

當我們在使用sqlite數據庫來實現表的1:1關聯(lián)時娶聘,通常我們會在主表中定義一個外鍵去關聯(lián)副表甚脉,當要查詢對應的數據時丸升,首先我們要知道查詢數據的外鍵,然后需要用外鍵去副表中查詢所需要的數據牺氨。比如下面這樣:

@Entity
public class Customer {
    private Long id;    private String username;
}
@Entity
public class Order {
    private Long orderId;
    private String orderInfo;
    private Long customerId;
}

Customer表通過orderId與Order表關聯(lián)狡耻,查詢Order的Customer時需要先知道Order中的customerId然后根據
Customer.id=Order .customerId值再去數據庫中查詢該orderId所對應的Customer對象。然而在greenDao中一個注釋就可以搞定猴凹,只需要使用@ToOne注釋來定義一個關聯(lián)對象即可夷狰。

  • @ToOne 定義了一個entities與另一個entities的1:1對應關系。通過joinProperty參數來定義一個外鍵下面是代碼示例:
@Entity
public class Customer {
    @Id(autoincrement = true)
    @Property(nameInDb = "CUSTOMERID")
    private Long customerId;
    @Property(nameInDb = "USERNAME")
    private String username;
}
@Entity
public class Order {
    @Id(autoincrement = true)
    @Property(nameInDb = "ORDERID")
    private Long orderId;
    @Property(nameInDb = "ORDERINFO")
    private String orderInfo;
    @Property(nameInDb = "CUSTOMERID")
    private Long customerId;

    @ToOne(joinProperty = "customerId")
    private Customer customer;
}

這樣只要獲得Order對象就能通過getCustomer()方法獲取Order所對應的Customer了郊霎,這樣是不是很高效孵淘,很簡便。其實getCustomer方法也很簡單歹篓,就是在底層幫助我們封裝好了查詢語句而已瘫证,另外getCustomer獲取的對象也是懶查詢機制,只有真正使用getCustomer方法查詢到的對象時greenDao才會執(zhí)行查詢操作庄撮。如果你想立即執(zhí)行查詢操作可以調用DAO類的loadDeep()與queryDeep()方法背捌。如果編譯的時候發(fā)生以下問題是因為沒有設置主鍵(@Id):

![沒有設置主鍵 . . .]


Paste_Image.png

1:N 關聯(lián)

在1對1關聯(lián)中每個顧客只能與一個訂單對應,但是現實生活中肯定不只是這樣洞斯,也會出現一個顧客下多個訂單的情況毡庆。如果出現這種需求的話,按照原生Sqlite的思路一樣是通過外鍵關聯(lián)即可烙如,只是這一次查詢的對象會有很多個么抗,但是使用greenDao的1:1關聯(lián)方式顯然不行。不過別擔心greenDao還給我們準備了@ToMany注釋亚铁。

  • @ToMany 定義了一個entities(這個標記為源實體)與另一個entities(這個標記為目標實體)的多個對象的關聯(lián)關系:@Tomany有一下三種方式來定義1:N的映射關系蝇刀。
  • referencedJoinProperty 在目標實體中我們需要定義一個與源實體關聯(lián)起來的外鍵,即Order中的customerId徘溢,然后需要在源實體里我們需要將customerId作為referencedJoinProperty的屬性吞琐。
public class Customer {
    @Id(autoincrement = true)
    @Property(nameInDb = "CUSTOMERID")
    private Long customerId;
    @Property(nameInDb = "USERNAME")
    private String username;
    @ToMany(referencedJoinProperty = "customerId")
    private List<Order> orders;
}

@Entity
public class Order {
    @Id(autoincrement = true)
    @Property(nameInDb = "ORDERID")
    private Long orderId;
    @Property(nameInDb = "ORDERINFO")
    private String orderInfo;
    @Property(nameInDb = "CUSTOMERID")
    private Long customerId;
}
  • joinProperties這個參數是referencedJoinProperty 參數的升級版捆探。在referencedJoinProperty參數中我們發(fā)現倆個實體關聯(lián)的外鍵是CustomerId與id,但是如果我們的需求是外鍵不能通過id來定義站粟,需要用自己自定義屬性來定義黍图,第一種方法就沒法用了,而joinProperties就是為了解決這個需求的奴烙。
  @Entity
  public class Customer {
      @Id private Long id;
      @Unique private String tag;

      @ToMany(joinProperties = {
              @JoinProperty(name = "tag", referencedName = "customerTag")
      })
      @OrderBy("date ASC")
      private List<Site> orders;
  }

  @Entity
  public class Order {
      @Id private Long id;
      private Date date;
      @NotNull private String customerTag;
  }

其實如果把

@ToMany(joinProperties = {
              @JoinProperty(name = "id", referencedName = "customerId")
      })

這樣的話就和第一種方法實現原理是一樣的了助被。

N:M關系

  • @JoinEntity 定義了N:M的映射關系。
  @Entity
  public class Product {
      @Id private Long id;

      @ToMany
      @JoinEntity(
              entity = JoinProductsWithOrders.class,
              sourceProperty = "productId",
              targetProperty = "orderId"
      )
      private List<Order> ordersWithThisProduct;
  }

  @Entity
  public class JoinProductsWithOrders {
      @Id private Long id;
      private Long productId;
      private Long orderId;
  }

  @Entity
  public class Order {
      @Id private Long id;
  }

關聯(lián)表的更新與解析

關聯(lián)的查詢也是懶加載機制切诀,而且查詢的結果會保存在緩存中下一次查詢的時候如果緩存有會直接從緩存中獲取結果恰起。

同樣關聯(lián)表更新時因為有緩存機制的存在你需要將改動的表手動的通過add()方法來更新關聯(lián)表中的對象或者直接清除緩存。

// 獲取當前顧客的訂單列表
List<Order> orders1 = customer.getOrders();

// 插入一個新訂單
Order order = new Order();
order.setCustomerId(customer.getId());
daoSession.insert(order);

// 再一次獲取顧客的訂單
List<Order> orders2 = customer.getOrders(); 

// 因為緩存列表沒有更新所以訂單1與訂單2的大小相等
assert(orders1.size() == orders2.size);
// 也是相同的對象
assert(orders1.equals(orders2));

//調用該方法后趾牧,才能更新緩存列表
orders1.add(newOrder);

//刪除時也許要手動將緩存列表里面的數據刪除
List orders = customer.getOrders();
// 從數據庫中移除
daoSession.delete(someOrder);
// 手動從緩存列表移除
orders.remove(someOrder);

//如果數據庫更新后不想手動添加數據可以使用resetXX()方法來清除緩存

customer.resetOrders();
List orders = customer.getOrders();

多表查詢

有些時候我們的表沒有使用ToOne與ToMany建立關聯(lián)關系检盼,但是我們又想一步到位。這時我們可以使用greenDao的多表查詢功能來幫助我們減少不必要的代碼翘单。

關聯(lián)單個表

//查詢地址是住在迪拜大樓的用戶
QueryBuilder<User> queryBuilder = userDao.queryBuilder();
queryBuilder.join(Address.class, AddressDao.Properties.userId)
  .where(AddressDao.Properties.Street.eq("迪拜大樓"));
List<User> users = queryBuilder.list();

關聯(lián)多個表

//查詢在歐洲人口超過100000的城市
QueryBuilder qb = cityDao.queryBuilder().where(Properties.Population.ge(1000000));
Join country = qb.join(Properties.CountryId, Country.class);
Join continent = qb.join(country, CountryDao.Properties.ContinentId,
Continent.class, ContinentDao.Properties.Id);
continent.where(ContinentDao.Properties.Name.eq("Europe"));
List<City> bigEuropeanCities = qb.list();

通過queryBuilder.join()鏈式調用來實現多表查詢
注意:多表查詢的前提是我們已經定義好了外鍵來關聯(lián)表與表之間的關系吨枉。


注解(不全,用到了再補充)

實體@Entity注解

  • schema:告知GreenDao當前實體屬于哪個schema哄芜;
  • active:標記一個實體處于活動狀態(tài)貌亭,活動實體有更新、刪除和刷新方法认臊;
  • nameInDb:在數據中使用的別名圃庭,默認使用的是實體的類名;
  • indexes:定義索引失晴,可以跨越多個列;
  • createInDb:標記創(chuàng)建數據庫表;

基礎屬性注解

  • @Id :主鍵 Long型剧腻,可以通過@Id(autoincrement = true)設置自增長;
  • @Property:設置一個非默認關系映射所對應的列名涂屁,默認是的使用字段名 舉例:@Property (nameInDb="name")书在;
  • @NotNul:設置數據庫表當前列不能為空;
  • @Transient :添加次標記之后不會生成數據庫表的列拆又;

索引注解

  • @Index:使用@Index作為一個屬性來創(chuàng)建一個索引儒旬,通過name設置索引別名,也可以通過unique給索引添加約束帖族;
  • @Unique:向數據庫列添加了一個唯一的約束栈源;

關系注解

  • @ToOne:定義與另一個實體(一個實體對象)的關系
  • @ToMany:定義與多個實體對象的關系

GreenDAO原理簡析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市竖般,隨后出現的幾起案子甚垦,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件制轰,死亡現場離奇詭異,居然都是意外死亡胞谭,警方通過查閱死者的電腦和手機垃杖,發(fā)現死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丈屹,“玉大人调俘,你說我怎么就攤上這事⊥荩” “怎么了彩库?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長先蒋。 經常有香客問我骇钦,道長,這世上最難降的妖魔是什么竞漾? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任眯搭,我火速辦了婚禮,結果婚禮上业岁,老公的妹妹穿的比我還像新娘鳞仙。我一直安慰自己,他們只是感情好笔时,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布棍好。 她就那樣靜靜地躺著,像睡著了一般允耿。 火紅的嫁衣襯著肌膚如雪借笙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天较锡,我揣著相機與錄音提澎,去河邊找鬼。 笑死念链,一個胖子當著我的面吹牛盼忌,可吹牛的內容都是我干的。 我是一名探鬼主播掂墓,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼谦纱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了君编?” 一聲冷哼從身側響起跨嘉,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吃嘿,沒想到半個月后祠乃,有當地人在樹林里發(fā)現了一具尸體梦重,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年亮瓷,在試婚紗的時候發(fā)現自己被綠了琴拧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘱支,死狀恐怖蚓胸,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情除师,我是刑警寧澤沛膳,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站汛聚,受9級特大地震影響锹安,放射性物質發(fā)生泄漏。R本人自食惡果不足惜倚舀,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一八毯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞄桨,春花似錦话速、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至柱查,卻和暖如春廓俭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唉工。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工研乒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淋硝。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓雹熬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谣膳。 傳聞我的和親對象是個殘疾皇子竿报,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容