Android ORM 框架 greenDao 3.x 第一篇: 介紹振峻,使用與踩坑

最近接觸greenDao3.x公般,總結(jié)一些業(yè)務(wù)中用到的東西和坑万搔。
greenDao 到3.x之后用法比2.x和1.x時(shí)候更加簡(jiǎn)單,通過(guò)對(duì)bean類增加注解實(shí)現(xiàn)持續(xù)化官帘。
許多文章介紹了大致情況瞬雹,如 http://www.reibang.com/p/f2737d23cb2a

如何上手和了解大致情況也可以閱讀greenDao的官方文檔 http://greenrobot.org/greendao/

其他文章介紹的東西就不再重復(fù)了,本文總結(jié)一下實(shí)際應(yīng)用中用到的一些特性和遇到的坑刽虹。
除了注解方式以外酗捌,實(shí)際上,3.X仍然支持像2.x中的generator的方式來(lái)生成相關(guān)類涌哲。并且generator支持更多新特性胖缤,如多schema,生成ContentProvider等阀圾,這些功能還不支持通過(guò)注解來(lái)實(shí)現(xiàn)哪廓。(見http://greenrobot.org/greendao/documentation/updating-to-greendao-3-and-annotations/

  1. greenDao3.x 原理簡(jiǎn)介:
    greenDao3.x引入了新的Gradle plugin:greendao-gradle-plugin 。用Gradle插件來(lái)生成所需要的類稍刀。greenrobot目前還沒把gradle插件源碼還沒放出來(lái)撩独〕ú埽可以見所有注解都是@Retention(RetentionPolicy.SOURCE)。gradle plugin就是根據(jù)這些注解综膀,為實(shí)體生成對(duì)應(yīng)的各種類澳迫。
    主要的類包括:
    (1) DaoMaster :Dao的頂層對(duì)象,包含DevOpenHelper內(nèi)部類剧劝,并可以新建DaoSession橄登。獲取
    (2) DaoSession :管理各個(gè)Dao類,用Map管理了各個(gè)Dao類的實(shí)例對(duì)象(key是Dao的類對(duì)象讥此,value是實(shí)例對(duì)象)拢锹。
    (3) xxxDao:與Entity一一對(duì)應(yīng),管理對(duì)應(yīng)的表萄喳,完成從對(duì)象到表的增刪改查卒稳。我們實(shí)際對(duì)實(shí)例對(duì)象的操作都是通過(guò)該Entity對(duì)應(yīng)的Dao對(duì)象完成的。

Database/DaoMaster/DaoSession對(duì)象的初始化過(guò)長(zhǎng)如下:

DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(application,     
DATABASE_NAME, null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
mDaoSession = daoMaster.newSession();

此外如果閱讀源碼還有xxxDaoConfig類他巨。但此類為GreenDao的內(nèi)部所用充坑,實(shí)際不會(huì)應(yīng)用到, 但對(duì)理解源碼比較有幫助染突。

  1. 相關(guān)注解的實(shí)際用法
    (1) Entity:該注解是給bean類的捻爷。注意了,greenDao目前只支持一個(gè)Entity對(duì)應(yīng)一張表(見https://github.com/greenrobot/greenDAO/issues/455#issuecomment-249790184)份企。
    一對(duì)多或者多對(duì)一目前還實(shí)現(xiàn)不了也榄。如果有類似需求可以用ToOne 和 ToMany 代替(下文會(huì)介紹)。
    Entity的屬性:
    indexes:可以用來(lái)創(chuàng)建該表的索引司志√鹱希可以與unique一同使用實(shí)現(xiàn)唯一索引,如:
@Entity(indexes = {
        @Index(value = "primaryCode, secondCode", unique = true)
})

nameInDb:自定義Entity對(duì)應(yīng)的表名(默認(rèn)是用類名)
createInDb:是否在數(shù)據(jù)庫(kù)中創(chuàng)建該表俐芯,源碼的注釋這么說(shuō)的:

    /**
     * Advanced flag to disable table creation in the database (when set to false). This can be used to create partial
     * entities, which may use only a sub set of properties. Be aware however that greenDAO does not sync multiple
     * entities, e.g. in caches.
     */

大意是在只需持久化某Entity類中的一部分屬性時(shí)使用棵介,不是特別理解钉鸯,實(shí)操確實(shí)沒創(chuàng)建該類的表吧史。但貌似是不能實(shí)現(xiàn)多個(gè)實(shí)體對(duì)應(yīng)到一張表里。希望有高人能解答該屬性有何用唠雕。
(2).Id:被修飾的域會(huì)成為表中的主鍵贸营,該Id對(duì)應(yīng)了xxxDao類中相關(guān)操作中的Key。
(3).Convert:修飾域岩睁,如果Entity中的一個(gè)域的類型不能直接保存(比如枚舉類型)或者該域的值與您想保存到數(shù)據(jù)庫(kù)中的類型不一樣的話钞脂,可以用Convert修改該域?qū)嶋H保存的類型。如下將Convert講時(shí)間(String)轉(zhuǎn)成了Long來(lái)保存捕儒。

public class TimeConverter implements PropertyConverter<String, Long> {
    private static final String TAG = "TimeConverter";
    static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public String convertToEntityProperty(Long databaseValue) {
        Log.d(TAG, "convertToEntityProperty:"+formatter.format(new Date(databaseValue)));
        return formatter.format(new Date(databaseValue));
    }

    @Override
    public Long convertToDatabaseValue(String entityProperty) {
        try {
            Log.d(TAG, "convertToDatabaseValue:"+formatter.parse(entityProperty).getTime());
            return formatter.parse(entityProperty).getTime();
        } catch (ParseException e) {
            Log.e(TAG, e.getMessage());
        }
        return null;
    }
}

(4).Index:與Indexs標(biāo)簽一起添加索引冰啃,見indexes標(biāo)簽邓夕。
(5).ToOne/ToMany:Entity的某個(gè)域是Entity,對(duì)應(yīng)數(shù)據(jù)庫(kù)的表連接阎毅。ToOne為一對(duì)一焚刚,ToMany為該域是List,對(duì)應(yīng)多個(gè)實(shí)體扇调。
要用joinProperty屬性指明連接的key矿咕,該域?qū)?yīng)被連接的Entity的Id。
例:

    @ToOne(joinProperty = "amountId")
    private PaymentAmount amount;
    private long amountId;

注意:這里保存和讀取時(shí)候都有坑:
當(dāng)存一個(gè)Entity的時(shí)候狼钮,與其ToOne和ToMany的Entity都不會(huì)被保存碳柱,需要單獨(dú)再去存每個(gè)對(duì)象,并且要在設(shè)置amountId :如:

    mPaymentAmountDao.insert(paymentAmount);
    record.setAmountId(record.getAmount().getId());
    mPaymentRecordDao.insert(record);
  1. 數(shù)據(jù)的增刪改查:與Entity對(duì)應(yīng)的Dao類提供了對(duì)應(yīng)每個(gè)Entity的相關(guān)方法熬芜。這里列舉一些常 用的:
public void save(T entity)
public void delete(T entity)
public void update(T entity)
public long insertOrReplace(T entity)
public void deleteAll()
public Long getKey(PaymentRecord entity) 
public boolean hasKey(PaymentRecord entity)
public List<T> loadAll()
public PaymentRecord loadDeep(Long key)
public List<PaymentRecord> queryDeep(String where, String... selectionArg)
public QueryBuilder<T> queryBuilder()

注意
(1) 如果該Entity有ToMany或者ToOne字段莲镣,該Dao類中會(huì)有xxxDeep的方法:該方法會(huì)一起獲取ToMany和ToOne字段,也就是其ToMany和ToOne字段不為空(查詢過(guò)程中會(huì)join相關(guān)的表)涎拉,如果用其他方法查詢?cè)搶?duì)象的ToMany和ToOne字段是null剥悟。
其中,queryDeep中的where和selectionArg直接傳給數(shù)據(jù)庫(kù)做查詢曼库,所以需要為數(shù)據(jù)庫(kù)的相關(guān)字段名而非實(shí)體的区岗,可見源碼:

    /** A raw-style query where you can pass any WHERE clause and arguments. */
    public List<PaymentRecord> queryDeep(String where, String... selectionArg) {
        Cursor cursor = db.rawQuery(getSelectDeep() + where, selectionArg);
        return loadDeepAllAndCloseCursor(cursor);
    }

(2) queryBuilder
這與SQLiteQueryBuilder不同,queryBuilder方法是直接對(duì)應(yīng)的Entity的字段的毁枯,而非數(shù)據(jù)庫(kù)慈缔。如:

      List<DayRecord> list = mDayRecordDao.queryBuilder()
              .where(DayRecordDao.Properties.CurrencyCode.eq(currencyCode))
              .where(DayRecordDao.Properties.Day.eq(day)).list();

(3) 這里查詢是有緩存的:比如:

Long id = record1.getId();//record1已保存過(guò)
Record record2 = mRecordDao.loadDeep(id);
boolean isSameObj = (record1 == record2) // true

如果record1被修改,還想讀原來(lái)數(shù)據(jù)庫(kù)中其值种玛,請(qǐng)?jiān)趌oadDeep前調(diào)用:daoSession.clear()藐鹤。

自動(dòng)生成的xxxDao類的代碼可見 dentityScope為緩存,

public abstract class AbstractDao<T, K> {
 ...
protected final IdentityScope<K, T> identityScope;
...
public AbstractDao(DaoConfig config, AbstractDaoSession daoSession) {
       ...
        identityScope = (IdentityScope<K, T>) config.getIdentityScope();
      ...
    }
}

daoSession.clear():

    public void clear() {
        xxxDaoConfig.clearIdentityScope();
    }
  1. 相關(guān)的坑:
    (1)不支持Entity繼承: Entity類在生成表和Dao類時(shí)候赂韵,是不支持其父類的相關(guān)屬性的娱节,即父類中的相關(guān)域不會(huì)在表中有對(duì)應(yīng)字段。這就導(dǎo)致了所有的model類都不能繼承祭示,導(dǎo)致有些model類比較類似肄满,也都需要都寫兩遍,破壞了代碼結(jié)構(gòu)质涛。稠歉。蛋疼
    (2)不支持一個(gè)Entity對(duì)應(yīng)多表,或多個(gè)Entity對(duì)應(yīng)成一個(gè)表。蛋疼如上
    (3)不支持一個(gè)字段convert成多字段汇陆,智能convert成一個(gè)其他字段怒炸。蛋疼如上
    (4)注解方式不支持provider生成:不能自動(dòng)生成provider。generator方式可以毡代,但provider的功能還沒完善阅羹。所以如果工程需要跨進(jìn)程提供數(shù)據(jù)則還需要手動(dòng)寫provider勺疼。

總結(jié):整體來(lái)講,GreenDao 作為對(duì)象持久化框架還是比較好用捏鱼,但不足也比較明顯:Entity的靈活性不足恢口,有時(shí)Bean類的一些由于業(yè)務(wù)上的設(shè)計(jì)并不適合直接轉(zhuǎn)成數(shù)據(jù)庫(kù)表存儲(chǔ),而Entity映射到數(shù)據(jù)庫(kù)表的方法靈活性不高穷躁,導(dǎo)致表結(jié)構(gòu)和Bean類的結(jié)構(gòu)要互相遷就耕肩。
接下來(lái)后續(xù)還會(huì)再去總結(jié)GreenDao的數(shù)據(jù)庫(kù)升級(jí)/加密等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末问潭,一起剝皮案震驚了整個(gè)濱河市猿诸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狡忙,老刑警劉巖梳虽,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異灾茁,居然都是意外死亡窜觉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門北专,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)禀挫,“玉大人,你說(shuō)我怎么就攤上這事拓颓∮镉ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵驶睦,是天一觀的道長(zhǎng)砰左。 經(jīng)常有香客問(wèn)我,道長(zhǎng)场航,這世上最難降的妖魔是什么缠导? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮溉痢,結(jié)果婚禮上僻造,老公的妹妹穿的比我還像新娘。我一直安慰自己适室,他們只是感情好嫡意,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布举瑰。 她就那樣靜靜地躺著捣辆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪此迅。 梳的紋絲不亂的頭發(fā)上汽畴,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天旧巾,我揣著相機(jī)與錄音,去河邊找鬼忍些。 笑死鲁猩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罢坝。 我是一名探鬼主播廓握,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘁酿!你這毒婦竟也來(lái)了隙券?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤闹司,失蹤者是張志新(化名)和其女友劉穎娱仔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體游桩,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牲迫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了借卧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盹憎。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖铐刘,靈堂內(nèi)的尸體忽然破棺而出脚乡,到底是詐尸還是另有隱情,我是刑警寧澤滨达,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布奶稠,位于F島的核電站,受9級(jí)特大地震影響捡遍,放射性物質(zhì)發(fā)生泄漏锌订。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一画株、第九天 我趴在偏房一處隱蔽的房頂上張望辆飘。 院中可真熱鬧,春花似錦谓传、人聲如沸蜈项。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)紧卒。三九已至,卻和暖如春诗祸,著一層夾襖步出監(jiān)牢的瞬間跑芳,已是汗流浹背轴总。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留博个,地道東北人怀樟。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像盆佣,于是被迫代替她去往敵國(guó)和親往堡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容