GreenDao —— 簡單快速操作 Android SQLite 數(shù)據(jù)庫
GreenDao 是輕量快速的 SQLite 數(shù)據(jù)庫 ORM 解決方案必怜。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.)
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)生成的方法:
- 無參構(gòu)造函數(shù)
- 有參構(gòu)造函數(shù)
- 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ǔ)吧