GreenDao —— 簡單快速操作 Android SQLite 數(shù)據(jù)庫

GreenDao —— 簡單快速操作 Android SQLite 數(shù)據(jù)庫

GreenDao 是輕量快速的 SQLite 數(shù)據(jù)庫 ORM 解決方案必怜。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.

GreenDao 層次

ORM(Object-Relationl Mapping)用于在關(guān)系型數(shù)據(jù)庫與對象之間做一個(gè)映射≤蹋可以使數(shù)據(jù)庫操作想對象一樣使用,而避開使用復(fù)雜的SQL語句交互胚委。

GreenDao 特點(diǎn):

  • 性能強(qiáng)大晨仑。(可能是 Android 平臺(tái)最快的 ORM 框架)
  • 簡易便捷的 API
  • 開銷小
  • 依賴體積小
  • 支持?jǐn)?shù)據(jù)庫加密
  • 強(qiáng)大的社區(qū)支持

此前接觸 Android 的 SQLite 數(shù)據(jù)庫操作,有感于直接使用 SQLite 繁瑣且低效烫罩,使用 Android 官方的 Room 也感覺效果不佳。最后選擇 GreenDao 總算滿足預(yù)期洽故。

GreenDao 環(huán)境配置

1. Project 下的 build.gradle 增加插件支持


buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 版本建議最新
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

2. app 下的 build.gradle 增加插件依賴


apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'

android {
    ...

    // greendao 配置
    greendao {
        schemaVersion 1                                         // 數(shù)據(jù)庫版本號
        daoPackage      'org.cvte.research.faceapi.greendao'    // greenDao 自動(dòng)生成的代碼保存的包名
        targetGenDir    'src/main/java'                         // 自動(dòng)生成的代碼存儲(chǔ)的路徑贝攒,默認(rèn)是 build/generated/source/greendao.
    }

    ...
}

dependencies {
    ...

    // GreenDao  數(shù)據(jù)庫ORM
    implementation 'org.greenrobot:greendao:3.2.2'
    // GreenDao 生成dao和model的generator的項(xiàng)目 發(fā)布時(shí)可以去掉
    implementation 'org.greenrobot:greendao-generator:3.2.2'
}

接下來可以使用 GreenDao 對數(shù)據(jù)庫對象進(jìn)行操作了。

創(chuàng)建 GreenDao 數(shù)據(jù)庫對象實(shí)體

1. 創(chuàng)建數(shù)據(jù)庫實(shí)體

創(chuàng)建實(shí)體需要了解 GreenDao 的注解:

注解 描述 其它參數(shù)
Entity 對應(yīng)數(shù)據(jù)庫中的表 nameInDb:表使用別名时甚。默認(rèn)為類名
active:標(biāo)記一個(gè)實(shí)體是否處于活動(dòng)狀態(tài)隘弊,活動(dòng)實(shí)體有 update、delete荒适、refresh 方法梨熙。默認(rèn)為 false
indexes:定義多列索引
Id 該數(shù)據(jù)庫表的主鍵,只能是 Long 或 long 類型 autoincrement:設(shè)置是否自增刀诬,可以通過傳入 null 自動(dòng)分配
Unique 唯一咽扇。可以通過設(shè)置唯一的屬性設(shè)為主鍵
Property 列名 nameInDb:列使用別名。默認(rèn)為變量名
Index 索引 unique:設(shè)置唯一
name:設(shè)置索引的別名
NotNull 非空质欲。該字段值不能為空
Transient 忽略树埠。greendao 將不會(huì)創(chuàng)建對應(yīng)的項(xiàng)
ToOne 表格映射關(guān)系一對一 joinProperty:外聯(lián)實(shí)體與該實(shí)體主鍵的匹配成員
ToMany 表格映射關(guān)系一對多或多對多
Generated greendao 產(chǎn)生的部分,手動(dòng)修改會(huì)報(bào)錯(cuò)
Keep 替換 Generated把敞,greendao不再生成和報(bào)錯(cuò)
Convert 數(shù)據(jù)類型轉(zhuǎn)換弥奸。實(shí)體類型與數(shù)據(jù)庫類型轉(zhuǎn)換榨惠,實(shí)現(xiàn)存儲(chǔ)和修改的便捷 converter:轉(zhuǎn)換方法
columnType:數(shù)據(jù)庫使用的數(shù)據(jù)類型

文件名為:UserBean.java


@Entity(nameInDb = "user_table")
public class UserBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "user_id")
    private Long userId;

    @NotNull
    @Property(nameInDb = "group_id")
    private Long groupId;

    @NotNull
    @Property(nameInDb = "user_name")
    private String userName;

    @Unique
    @NotNull
    @Property(nameInDb = "user_number")
    private String userNumber;
}

另外增加一個(gè) GroupBean.java 實(shí)體奋早,內(nèi)容如下:


@Entity(nameInDb = "group_table")
public class GroupBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "group_id")
    private Long groupId;

    @Unique
    @NotNull
    @Property(nameInDb = "group_name")
    private String groupName;
}

2. 編譯后,生成完善的數(shù)據(jù)庫實(shí)體方法

編譯后可以看到 UserBean.java 文件增加了不少接口赠橙。


@Entity(nameInDb = "user_table")
public class UserBean {
    @Id(autoincrement = true)
    @Unique
    @Property(nameInDb = "user_id")
    private Long userId;

    @NotNull
    @Property(nameInDb = "group_id")
    private Long groupId;

    @NotNull
    @Property(nameInDb = "user_name")
    private String userName;

    @Unique
    @NotNull
    @Property(nameInDb = "user_number")
    private String userNumber;

    @Generated(hash = 1853997691)
    public UserBean(Long userId, @NotNull Long groupId, @NotNull String userName,
            @NotNull String userNumber) {
        this.userId = userId;
        this.groupId = groupId;
        this.userName = userName;
        this.userNumber = userNumber;
    }

    @Generated(hash = 1203313951)
    public UserBean() {
    }

    public Long getUserId() {
        return this.userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Long getGroupId() {
        return this.groupId;
    }

    public void setGroupId(Long groupId) {
        this.groupId = groupId;
    }

    public String getUserName() {
        return this.userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserNumber() {
        return this.userNumber;
    }

    public void setUserNumber(String userNumber) {
        this.userNumber = userNumber;
    }
}

自動(dòng)生成的方法:

  1. 無參構(gòu)造函數(shù)
  2. 有參構(gòu)造函數(shù)
  3. getter / setter 方法

除此之外耽装,還生成了 DaoMaster,DaoSession期揪,UserBeanDao (GroupBean 對應(yīng)生成 GroupBeanDao ) 等文件掉奄。
下面來介紹各自的功能。

3. DaoMaster凤薛,DaoSession姓建,Dao 文件

文件 描述 相應(yīng)文件
DaoMaster 保存數(shù)據(jù)庫對象(SQLiteDatabase) DaoMaster
DaoSession 管理所有的 Dao 對象 DaoSession
Dao 數(shù)據(jù)訪問對象(Data Access Object),可以通過 Dao 操作數(shù)據(jù)實(shí)體 UserBeanDao缤苫、GroupBeanDao
Entity 數(shù)據(jù)實(shí)體(每個(gè)實(shí)體對應(yīng)數(shù)據(jù)庫內(nèi)的一個(gè)表) UserBean速兔、GroupBean

通過 GreenDao 操作數(shù)據(jù)庫。

1. 初始化數(shù)據(jù)庫


private static DaoSession mDaoSession;

public initDatabase(Context context, String databaseFileName) {
    DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, databaseFileName, null);
    SQLiteDatabase db = helper.getWritableDatabase();
    DaoMaster daoMaster = new DaoMaster(db);
    mDaoSession = daoMaster.newSession();

    // 打開查詢的LOG
    // QueryBuilder.LOG_SQL = true;
    // QueryBuilder.LOG_VALUES = true;
}

public static DaoSession getDaoSession() {
    return mDaoSession;
}

執(zhí)行 initDatabase 初始化數(shù)據(jù)庫后活玲,可以通過 getDaoSession 拿到 DaoSession 對每個(gè)數(shù)據(jù)庫實(shí)體進(jìn)行處理涣狗。

2. 插入數(shù)據(jù)

以以下數(shù)據(jù)為例:

組織(GroupName) 名字(UserName) 編號(UserNumber)
CHN LiTianYu 1-0090
USA LiBill 10-8082
XYZ MiXue 2-720

public void insertData(String groupName, String userName, String userNumber) {
    GroupBean groupBean = new GroupBean(null, groupName);                       // Group 主鍵(groupId)是自動(dòng)增加的,使用 null 就可以自增了舒憾。
    long keyGroupBean = getDaoSession().getGroupBeanDao().insert(groupBean);    // 返回值是插入庫后的 Group 實(shí)體的 key
    groupBean = getDaoSession().getGroupBeanDao().load(keyGroupBean);           // 通過 key 可以獲取到插入的 Group 數(shù)據(jù)
    
    UserBean userBean = new UserBean(null, groupBean.getGroupId(), userName, userNumber);  // User 主鍵(userId)也是自動(dòng)增加的镀钓,但是 groupId 需要通過關(guān)聯(lián)的 Group 獲取。
    getDaoSession().getUserBeanDao().insert(userBean);
}

3. 刪除數(shù)據(jù)


// 刪除單個(gè)數(shù)據(jù)
getDaoSession().getUserBeanDao().delete(userBean);

// 刪除多個(gè)數(shù)據(jù) (userBeanList 類型為 List<UserBean>)
getDaoSession().getUserBeanDao().deleteInTx(userBeanList);

// 刪除所有數(shù)據(jù)
getDaoSession().getUserBeanDao().deleteAll();

4. 查找數(shù)據(jù)


// 查詢 userNumber 為 "1-0090" 的 user
getDaoSession().getUserBeanDao().queryBuilder().where(UserBeanDao.Properties.UserNumber.eq("1-0090")).unique();

// 查詢 userName 形如 "Lixxx" 的 user ( 類似于 SQLite 的 like 模糊查詢語法 )
getDaoSession().getUserBeanDao().queryBuilder().where(UserBeanDao.Properties.UserNumber.eq("Li%")).unique();

// 查詢 groupName 為 "CHN" 的 user ( 關(guān)聯(lián)表查詢 )
QueryBuilder<UserBean> qb = getDaoSession().getUserBeanDao().queryBuilder();    // 需要獲取 User 數(shù)據(jù)镀迂,所以 qb 為 User
Join join_UserBean = qb.join(UserBean.class, UserBeanDao.Properties.UserId);    // 設(shè)置 User 的關(guān)聯(lián)規(guī)則(根據(jù) UserBean.userId == UserBean.userId)
Join join_GroupBean = qb.join(join_UserBean, UserBeanDao.Properties.GroupId, GroupBean.class, GroupBeanDao.Properties.GroupId);     // 設(shè)置 Group 的關(guān)聯(lián)規(guī)則(根據(jù) UserBean.groupId == GroupBean.groupId)
join_GroupBean.where(GroupBeanDao.Properties.GroupName.eq("CHN"));      // 其它查找條件(User和Group已經(jīng)關(guān)聯(lián)起來了)
qb.list();      // 返回查找結(jié)果

使用 .unique() 為獲取查詢滿足要求的第一個(gè)數(shù)據(jù)丁溅。
使用 .list() 為獲取所有滿足要求的數(shù)據(jù)(返回結(jié)果為 List<> 類型)

多表關(guān)聯(lián)查詢稍顯復(fù)雜,可以通過 ToOne探遵、ToMany 設(shè)置表與表之間的關(guān)系進(jìn)行直接訪問的查詢窟赏。
但是大數(shù)據(jù)量時(shí)效率沒有使用以上方法快。

5. 更改數(shù)據(jù)


// 更改單個(gè)數(shù)據(jù)
getDaoSession().getUserBeanDao().update(userBean);

// 更改多個(gè)數(shù)據(jù) (userBeanList 類型為 List<UserBean>)
getDaoSession().getUserBeanDao().updateInTx(userBeanList);

其它

1. 類型轉(zhuǎn)換

SQLite 數(shù)據(jù)庫的數(shù)據(jù)類型有限(甚至不支持float)别凤,而作為對象則允許所有java的類型(數(shù)組饰序、各種類等)。
因此在 GreenDao 中支持類型轉(zhuǎn)換(從數(shù)據(jù)庫數(shù)據(jù)類型轉(zhuǎn)換為實(shí)體的數(shù)據(jù)類型)规哪,方便對實(shí)體進(jìn)行修改查詢求豫。

請?jiān)趯?yīng)的成員中加入 @Convert ,如:


// 以實(shí)體的 float[] 與 數(shù)據(jù)庫的 TEXT 類型轉(zhuǎn)換為例
@Convert(converter = ConvertFloatArrayToString.class, columnType = String.class)
private float[] featureData;

轉(zhuǎn)換方法:


public class ConvertFloatArrayToString implements PropertyConverter<float[], String> {
    // 數(shù)據(jù)庫類型 -> 實(shí)體類型
    public float[] convertToEntityProperty(String databaseValue) {
        String[] strList = databaseValue.split(",");
        float[] floatList = new float[strList.length];
        for (int i = 0, len = strList.length; i < len; ++ i) {
            floatList[i] = Float.parseFloat(strList[i]);
        }
        return floatList;
    }

    // 實(shí)體類型 -> 數(shù)據(jù)庫類型
    public String convertToDatabaseValue(float[] entityProperty) {
        String str = "" + entityProperty[0];
        for (int i = 1, len = entityProperty.length; i < len; ++ i) {
            str += "," + entityProperty[i];
        }
        return str;
    }
}

這樣就可以讀取直接操作 float[] 進(jìn)行讀寫,不需要每次都手動(dòng)轉(zhuǎn)為 TEXT(對應(yīng) Java 中的 String)或者解析 TEXT 了蝠嘉。

2. 其它的其它

比如加密最疆,比如緩存,比如懶加載蚤告。。杜恰。有空再補(bǔ)吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末获诈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子心褐,更是在濱河造成了極大的恐慌舔涎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逗爹,死亡現(xiàn)場離奇詭異亡嫌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掘而,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門挟冠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袍睡,你說我怎么就攤上這事知染。” “怎么了女蜈?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵持舆,是天一觀的道長。 經(jīng)常有香客問我伪窖,道長逸寓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任覆山,我火速辦了婚禮竹伸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘簇宽。我一直安慰自己勋篓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布魏割。 她就那樣靜靜地躺著譬嚣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钞它。 梳的紋絲不亂的頭發(fā)上拜银,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天殊鞭,我揣著相機(jī)與錄音,去河邊找鬼尼桶。 笑死操灿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泵督。 我是一名探鬼主播趾盐,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼小腊!你這毒婦竟也來了救鲤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤溢豆,失蹤者是張志新(化名)和其女友劉穎蜒简,沒想到半個(gè)月后瘸羡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漩仙,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年悠轩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了青灼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捏境。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖麸折,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粘昨,我是刑警寧澤垢啼,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站张肾,受9級特大地震影響芭析,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吞瞪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一馁启、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芍秆,春花似錦惯疙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荆虱,卻和暖如春蒿偎,著一層夾襖步出監(jiān)牢的瞬間俭缓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工酥郭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留华坦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓不从,卻偏偏與公主長得像惜姐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子椿息,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355