GreenDao
GreenDao是一個高效的數(shù)據(jù)庫訪問ORM框架,節(jié)省了自己編寫SQL的時間追驴,快速的增刪查改等操作。
介紹就不多說疏之,直接介紹重點吧5钛!体捏!首先po一個github的地址:https://github.com/greenrobot/greenDAO
配置GreenDao
// In your root build.gradle file:
buildscript {
repositories {
jcenter()
mavenCentral() // add repository
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin
}
}
// In your app projects build.gradle file:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
dependencies {
compile 'org.greenrobot:greendao:3.2.2' // add library
}
注意咯冠摄,一個是添加在工程的build.gradle糯崎,一個是添加到module的build.gradle文件中.
build.gradlel里面對greenDao進(jìn)行配置几缭。配置之后就搭建好了greenDao的環(huán)境,可以自動生成相關(guān)的類沃呢。
然后你需要配置greenDao的數(shù)據(jù)庫版本號以及自動生成的包名的路徑年栓,當(dāng)然路徑可選擇性的配置。
android{
...
}
greendao{
schemaVersion 2 // 數(shù)據(jù)庫版本號
daoPackage 'com.doris.sample.greendao'//greenDao 自動生成的代碼保存的包名
targetGenDir 'src/main/java' //自動生成的代碼存儲的路徑薄霜,默認(rèn)是 build/generated/source/greendao.
generateTests false //true的時候自動生成測試單元
targetGenDirTests: 測試單元的生成目錄默認(rèn)是 src/androidTest/java
}
接下來你定義自己的entity并且make project就可以開始對數(shù)據(jù)庫進(jìn)行操作啦某抓。
下面對greenDao的相關(guān)知識點進(jìn)行介紹:
注釋
greenDAO 3 用注釋去schemas 和實體類 entities.例子如下:
@Entity(nameInDb = "user_info",indexes = {
@Index(value = "name DESC", unique = true)
},)
public class User {
@Id
private Long id;
@Property(nameInDb = "USERNAME") @NotNull
private String name;
@Transient
private int tempUsageCount; // not persisted
// getters and setters for id and user ...
}
@Entity
將一個Java類轉(zhuǎn)變成一個實體類惰瓜。greenDao會根據(jù)這個生成對應(yīng)的代碼否副。PS: 必須是java類,kotlin不支持崎坊。
在Entity中我們可以配置許多信息备禀,比如nameInDb
是聲明了該表數(shù)據(jù)庫中的表名。
indexes 用于建立索引奈揍,索引的應(yīng)用場景可用于曲尸,當(dāng)你的表有多個主鍵的時候,來標(biāo)志一條數(shù)據(jù)的唯一性男翰,配合unique另患。
當(dāng)然上面兩個只是我們常用的屬性,還有幾個其他屬性蛾绎,目前我還沒有用到:
schema = "myschema", 當(dāng)你有多個schema昆箕,用這個屬性來告訴數(shù)據(jù)庫當(dāng)前entity屬于哪個schema鸦列。
active = true,用于標(biāo)志某個entity是否是active的鹏倘,active的實體類有刪改的方法敛熬。默認(rèn)是fale, 為true的時候會自動生成下面的代碼在entity里面:
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 128553479)
public void delete() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.delete(this);
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 1942392019)
public void refresh() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.refresh(this);
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 713229351)
public void update() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.update(this);
}
基本的屬性:
@Id 標(biāo)志主鍵
@Transient 表示不存儲在數(shù)據(jù)庫中
@NotNull 標(biāo)志這個字段不能是null
@Property 如果定義了這個屬性第股,那么nameInDb的值就是該列在數(shù)據(jù)表里面应民,該列的名稱。上面的例子夕吻,username的值存儲在數(shù)據(jù)表里面的USERNAME
那一列诲锹。
主鍵的限制
當(dāng)前,entity必須有一個long或者Long的屬性作為主鍵涉馅,但是有時候我們的主鍵不一定是long或者Long型的可能是string呀之類的归园,這個時候我們就可以定義索引屬性并且注明獨一無二。 如下:
@Index(name = "keyword", unique = true)
private String key;
name用于指明這個索引的列名稚矿,unique表示這個指是不可重復(fù)的庸诱。
@Unique 用于標(biāo)志列的值的唯一性。
greenDao盡最大的可能的合理的安排每個屬性的默認(rèn)值晤揣,所以一般不需要去配置每一個選項桥爽。只需要配置你需要的就可以了。
當(dāng)你編寫好的entity之后昧识,只需要點擊Build-> make project就可以自動生成相關(guān)的代碼了钠四。
如果你改變了已經(jīng)存在的entity記得rebuild工程保證舊的entity類被clean掉了。
記住不要隨意修改注明了@Generated的代碼跪楞,這樣會導(dǎo)致報錯缀去,
Error:Execution failed for task ':app:greendao'.
> Constructor (see ExampleEntity:21) has been changed after generation.
Please either mark it with @Keep annotation instead of @Generated to keep it untouched,
or use @Generated (without hash) to allow to replace it.
解決這個的辦法有兩個:
- 將@Generated注釋改回去,或者將自動生成的代碼刪掉甸祭,然后他會自動rebuild在下次build工程的時候缕碎。
- 將@Generated改成@Keep,這樣greenDao將不會檢查這段代碼池户,但是這樣可能會導(dǎo)致實體和greenDao其他的契合部分咏雌。另外,未來版本的greenDAO可能會在生成的方法中使用不同的代碼煞檩。最好是不要修改這個处嫌,如果實在要修改,建議編寫測試單元斟湃,測試一下確保不會有問題熏迹。
@Keep在新版本里面已經(jīng)被淘汰了,如果當(dāng)gradle檢測到有@Keep會將他替換成@TransientDW怠坛缕!注意啦!@琛赚楚!
實體類建立好了,那么如何操作數(shù)據(jù)庫骗卜,通過什么訪問數(shù)據(jù)庫呢宠页,先來介紹一下greenDao的幾個關(guān)鍵的類。
關(guān)鍵的幾個類
DaoMaster
greenDao的入口寇仓,持有數(shù)據(jù)庫(SQLiteDatabase)對象 并且 管理Schema的DAO類(不是對象)举户。持有靜態(tài)的創(chuàng)建于刪除表的方法。內(nèi)部類OpenHelper以及DevOpenHelper實現(xiàn)了SQLiteOpenHelper遍烦,創(chuàng)建了數(shù)據(jù)庫模式(Schema)俭嘁。進(jìn)行數(shù)據(jù)庫連接。
DaoSession
管理特定模式的所有可用的DAO對象服猪,通過getter方法可以獲取到DAO對象供填。提供了增刪查改實體的方法。
DAOs
全稱Data access Objects罢猪,數(shù)據(jù)訪問對象近她,對于每一個實體類,greenDao生成一個DAO,持有很多持久性的方法坡脐,例如 count泄私, loadAll以及insertInTx。
Entities
持久化對象(與數(shù)據(jù)庫對應(yīng)的實體類)备闲,對應(yīng)數(shù)據(jù)庫里面的一行。
在用數(shù)據(jù)庫之前首先需要初始化數(shù)據(jù)庫捅暴,一般我們在Application里面進(jìn)行數(shù)據(jù)庫的數(shù)據(jù)化恬砂,并且只初始化一次。
// do this once, for example in your Application class
helper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
db = helper.getWritableDatabase();
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
然后要操作對應(yīng)的對象的時候只需要獲取到對應(yīng)的Dao對象即可:
// do this in your activities/fragments to get hold of a DAO
noteDao = daoSession.getNoteDao();
Session
DaoSession
是greenDao重要的接口之一蓬痒,提供了基本的實體類操作泻骤,Daos提供了更多的完整的操作。
獲取Session
daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
請注意梧奢,數(shù)據(jù)庫連接屬于DaoMaster狱掂,因此多個會話引用相同的數(shù)據(jù)庫連接。 因此亲轨,可以很快創(chuàng)建新的會話趋惨。 但是,每個會話都會分配內(nèi)存惦蚊,通常是實體的會話“緩存”器虾。
Identity scope and session “cache”
如果有兩個查詢返回相同的數(shù)據(jù)庫對象讯嫂,那么您使用的Java對象有多少個:一個還是兩個?它完全取決于身份范圍兆沙。
greenDAO中的默認(rèn)值(行為是可配置的)是多個查詢返回對相同Java對象的引用欧芽。例如,從ID為42的USER表中加載一個User對象為這兩個查詢返回相同的Java對象葛圃。
這個副作用是某種實體“緩存”千扔。如果一個實體對象仍然在內(nèi)存中(greenDAO在這里使用弱引用),則該實體不會再構(gòu)造库正。另外昏鹃,greenDAO不執(zhí)行數(shù)據(jù)庫查詢來更新實體值。相反诀诊,對象會從會話緩存中“立即”返回洞渤,這會比一個或兩個數(shù)量級更快。
清除identity scope
清除整個session属瓣,沒有緩存對象返回
daoSession.clear();
清除單個Dao的identity scope 载迄,這樣某個dao不會有緩存對象返回
noteDao = daoSession.getNoteDao();
noteDao.detachAll();
Queries
在GreenDao,你可以自己寫原生的查詢語句抡蛙,也可以通過QueryBuilder
API來查詢护昧。
Query
用于查詢的類
- QueryBuilder
SQL
的語法錯誤只有在run time
的時候才會提示,但是用greenDao
的QueryBuilder
可以在編譯的時候就檢測到錯誤粗截。
查詢范例:
List<User> joes = userDao.queryBuilder()
.where(Properties.FirstName.eq("Joe"))
.orderAsc(Properties.LastName)
.list();
用對應(yīng)的Dao對對應(yīng)的表進(jìn)行查詢操作惋耙,調(diào)用qureyBuilder
方法,where
是查詢條件熊昌,可以有多個查詢條件绽榛,用逗號分隔開來,如下:
List<User> joes = userDao.queryBuilder()
.where(Properties.FirstName.eq("Joe"), Properties.AGE.eq("10")
.orderAsc(Properties.LastName)
.list();
上面語句查詢lastname是joe的并且年齡是10歲的人婿屹。
復(fù)雜的不是and連接的查詢語句可以如下查詢:
多個條件查詢:
QueryBuilder<User> qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq("Joe"),
qb.or(Properties.YearOfBirth.gt(1970),
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10))));
List<User> youngJoes = qb.list();
上面語句應(yīng)該如下解釋
First name is "Joe" AND (year of birth is greater than 1970 OR (year of birth is 1970 AND month of birth is equal to or greater than 10))
即找出first name是joe的然后出生于1970年10以后的灭美。
order
排序,我們可以對查詢的結(jié)果進(jìn)行排序昂利。
orderAsc(依據(jù)的屬性名稱届腐,可以有多個屬性),正序 orderDesc 倒序
// order by last name
queryBuilder.orderAsc(Properties.LastName);
// in reverse
queryBuilder.orderDesc(Properties.LastName);
// order by last name and year of birth
queryBuilder.orderAsc(Properties.LastName).orderDesc(Properties.YearOfBirth);
greenDAO使用的默認(rèn)排序規(guī)則是COLLATE NOCASE蜂奸,盡管它可以使用stringOrderCollation()進(jìn)行定制犁苏。 有關(guān)影響結(jié)果順序的其他方法,請參見QueryBuilder類文檔扩所。
limit
當(dāng)你根據(jù)條件查詢围详,返回N條數(shù)據(jù)的時候,但是你只想要里面的前十個的時候碌奉,這個時候用limit
可以限制獲取 n
條數(shù)據(jù)短曾,不用for循環(huán)cursor寒砖。
limit
limit(int)
限制查詢返回的條數(shù)。
需求千變?nèi)f化的嫉拐,有時候你需要的不是前面的n
條哩都,而是從位置m
開始的n
條數(shù)據(jù),這個時候就可以用offset
設(shè)置數(shù)據(jù)返回的偏移值婉徘。
offset(int)
設(shè)置返回數(shù)據(jù)的起始偏移值漠嵌,得到offset開始的n條數(shù)據(jù)。必須要結(jié)合limit(int n)來使用
queryBuilder().where(Properties.YearOfBirth.gt(2001)).offset(3).limit(10)
上面例子是取出出生年份大于2001年的第3個開始的十個人盖呼。
查詢條件總結(jié)
-
eq
:Properties.YearOfBirth.eq(2001):
等于 -
notEq
: 不等于 -
like
:模糊查詢 記住模糊查詢儒鹿,string要用夾在%key%中間。
xxDao.queryBuilder().where(Properties.FirstName.like("%doris%")).list();
查詢FristName包含doris的人几晤。
-
BETWEEN ... AND ...
...和...之間约炎。 -
IN(..., ..., ...)
在給出的value的范圍內(nèi)的符合項 -
gt
大于 -
ge
大于等于 -
lt
小于 -
le
小于等于 -
isNull
不是空的
查詢有懶查詢,也叫延遲查詢吧蟹瘾,我覺得圾浅,就會你先執(zhí)行了查詢語句但是呢只有在你用的時候才會加載到內(nèi)存,就類似于懶加載憾朴。
-
list()
不是懶加載狸捕,正常查詢,查詢結(jié)果立馬加載到內(nèi)存众雷。 -
listLazy()
實體按需加載到內(nèi)存中灸拍。 一旦列表中的元素第一次被訪問,它將被加載并緩存以供將來使用砾省。 必須關(guān)閉鸡岗。 -
listLazyUncached()
一個“虛擬”實體列表:對列表元素的任何訪問都會導(dǎo)致從數(shù)據(jù)庫加載其數(shù)據(jù)。 必須關(guān)閉纯蛾。 -
listIterator()
讓我們通過按需加載數(shù)據(jù)(懶惰地)來遍歷結(jié)果纤房。 數(shù)據(jù)沒有被緩存。 必須關(guān)閉翻诉。
greenDao還支持多線程查詢以及重復(fù)使用Query對象,這個我還沒用到捌刮,用到給大家再深入講解一下哈碰煌!
原始查詢
方法1:
Query<User> query = userDao.queryBuilder().where(
new StringCondition("_ID IN " +
"(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)")
).build();
方法2:
queryRaw
和queryRawCreate
Query<User> query = userDao.queryRawCreate(
", GROUP G WHERE G.NAME=? AND T.GROUP_ID=G._ID", "admin"
);
debug查詢
QueryBuilder.LOG_SQL = true;
QueryBuilder.LOG_VALUES = true;
設(shè)置這兩個屬性就可以看到log、