簡介
GreenDao可以說是當今最流行谁撼,最高效而且還在迭代的關(guān)系型數(shù)據(jù)庫。
關(guān)系型數(shù)據(jù)庫:ORM(Object Relation Mapping 即 對象關(guān)系映射)狞甚,就是將面向?qū)ο缶幊陶Z言里的對象與數(shù)據(jù)庫關(guān)聯(lián)起來的一種技術(shù)及志,greenDao其實就是一種將java object 與SQLite Database關(guān)聯(lián)起來的橋梁
配置不介紹了,參考官網(wǎng)即可加袋。
優(yōu)點
1.存取速度快
2.支持數(shù)據(jù)庫加密
支持android原生的數(shù)據(jù)庫SQLite,也支持SQLCipher(在SQLite基礎(chǔ)上加密型數(shù)據(jù)庫)抱既。
3.輕量級
greenDao的代碼庫僅僅100k大小
4.激活實體
處于激活狀態(tài)下的實體可以有更多操作方法
5.支持緩存
能夠?qū)⑹褂玫倪^的實體存在緩存中职烧,下次使用時可以直接從緩存中取,這樣可以使性能提高N個數(shù)量級
6.代碼自動生成
DaoMaster
DaoMaster 是入口類防泵,每一個DaoMaster持有一個數(shù)據(jù)庫連接蚀之,通過DaoMaster#newSession()方法可以實例化多個Session,這些Session對應(yīng)同一個數(shù)據(jù)庫連接捷泞,但是系統(tǒng)會為每一個Session分配內(nèi)存足删,在這片內(nèi)存中會為實體進行緩存。每一個Session對應(yīng)一個Identity .
DaoSession
從DaoSession中可以獲取各實體類的對應(yīng)DAO锁右,然后就可以進行增刪改查的操作了失受,對于每一個Session中的查詢操作都會對查到的實體類做緩存操作,所以對應(yīng)同一個Session的多次查詢操作咏瑟,如果entity的對象在該Session中有對應(yīng)緩存則直接使用拂到,而不再從數(shù)據(jù)庫中讀取數(shù)據(jù)并構(gòu)建新的實體類對象。
常用注解介紹
@Entity
該實例是類的注解码泞,告訴greenDAO這個類是需要持久化的實體類兄旬。下面是其內(nèi)部注解
@Entity(
// If you want to have more than one schema, you can tell greenDAO
// to which schema an entity belongs (pick any string as a name).
schema = "myschema",
// Flag to make an entity "active": Active entities have update,
// delete, and refresh methods.
active = true,
// Specifies the name of the table in the database.
// By default, the name is based on the entities class name.
nameInDb = "AWESOME_USERS",
// Define indexes spanning multiple columns here.
indexes = {
@Index(value = "name DESC", unique = true)
},
// Flag if the DAO should create the database table (default is true).
// Set this to false, if you have multiple entities mapping to one table,
// or the table creation is done outside of greenDAO.
createInDb = false
)
@ID, field注解
表示選擇一個long或Long類型的屬性作為該實體所對應(yīng)數(shù)據(jù)庫中數(shù)據(jù)表的主鍵,參數(shù)可以設(shè)置(autoincreament=true),其實其他類型高版本也是可以的余寥,比如String 不過應(yīng)該不能自增長了领铐。
@Property field 注解
可以自定義該屬性在數(shù)據(jù)表的列名悯森,默認的列名為屬性名大寫,并由下劃線隔開多個單詞罐孝,@Property(nameInDb="XXXX")可以自定義列名呐馆。
@NotNull
對應(yīng)著數(shù)據(jù)表中該列不能為空。
@Transient
與Java中的Transient關(guān)鍵字類似莲兢,指不對該屬性持久化汹来,即不為該屬性在數(shù)據(jù)表中創(chuàng)建相應(yīng)列。
@Index
使用@Index 可以將一個屬性變?yōu)閿?shù)據(jù)庫索引改艇;其有倆個參數(shù)
name :不使用默認名稱收班,自定義索引名稱
unique : 給索引增加一個唯一約束,迫使該值唯一
@Entity
public class User {
@Id
private Long id;
@Index(unique = true)
private String name;
}
@Unique
含義與數(shù)據(jù)表中列的unique一致, 這種情況下谒兄,SQLite會自動為該列建立索引摔桦。
@Generated greenDAO
會根據(jù)開發(fā)者定義的實體類定義schema,并生成DAOs,而在生成過程中會為開發(fā)者編寫的實體類的一些方法和域上添加
@Generated注解
提示開發(fā)者該屬性不能被修改承疲;并且實體類的方法邻耕,屬性,構(gòu)造器一旦被@Generated注釋就不能被再次修改燕鸽,否則或報錯
此外還有@ToOne, @ToMany, @JoinEntity與多個數(shù)據(jù)表的關(guān)系有關(guān)兄世,后面再對此詳細介紹。
@Entity
public class User {
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "USERNAME")
private String name;
@NotNull
private int repos;
@Transient
private int tempUsageCount;
...}
查詢list
1.list()
緩存查詢結(jié)果;list()類型一般為ArrayList
2.listLazy();
懶查詢,只有當調(diào)用list()中的實體對象時才會執(zhí)行查詢操作并且只緩存第一次被查詢的結(jié)果啊研,需要關(guān)閉
3.listlazyUncached() 懶查詢,只有當調(diào)用list()中的實體對象時才會執(zhí)行查詢操作并且不緩存;
4.listIterator() 對查詢結(jié)果進行遍歷御滩,不緩存,需要關(guān)閉;
List<City> cityList =EntityManager.getInstance().getCityDao().queryBuilder().list();
LazyList<City> cityList2 =EntityManager.getInstance().getCityDao().queryBuilder().listLazy();
cityList2.close();
List<City> cityList3 =EntityManager.getInstance().getCityDao().queryBuilder().listLazyUncached();
CloseableListIterator<City> cityList4 =EntityManager.getInstance().getCityDao().queryBuilder().listIterator();
cityList4.close();//需要try
分頁查詢
limit(int): 限制查詢的數(shù)量;
offset(int): 每次返回的數(shù)量; offset不能單獨使用党远;
多次查詢:
即 第一次查詢返回的 query 可以再次設(shè)置條件削解;
// fetch users with Joe as a first name born in 1970
Query<User> query = userDao.queryBuilder().where(
Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)
).build();
List<User> joesOf1970 = query.list();
// using the same Query object, we can change the parameters
// to search for Marias born in 1977 later:
query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List<User> mariasOf1977 = query.list();
關(guān)聯(lián)查詢 joinI();
@Entity
public class City {
@Id
private Long id;
private String name;
private int population;
private Long countryId;
}
@Entity
public class Country {
@Id
private Long id;
private String name;
}
city 表示持有country 表的id
QueryBuilder<Country> qb=EntityManager.getInstance().getCountryDao().queryBuilder();
Join cities= qb.join(City.class,CityDao.Properties.CountryId)
.where(CityDao.Properties.Population.gt(1000));
List<Country> countries =qb.list();
就是 join on 只不過這里它替你寫了 on 的部分,應(yīng)該只是 id 才可以沟娱。
多線程查詢
多條線程執(zhí)行查詢語句時需要調(diào)用forCurrentThread()方法將query對象與當前線程進行綁定氛驮,如果其他線程修改該Query對象,greenDao將會拋出一個異常;forCurrentThread()方法通過將Query創(chuàng)建時的時間作為 query標識;
先看一段代碼
final Query<Country> query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
new Thread(new Runnable() {
@Override
public void run() {
List<Country> countries =query.list();
for (Country c:countries){
Log.e("返回",c.getName()+Thread.currentThread().getName());
}
}
}).start();
執(zhí)行后會報錯济似,當前的query 已經(jīng)綁定到主線程矫废,這時候在子線程中調(diào)用query 會報錯
08-08 15:56:44.040 4587-4614/com.green.dao E/AndroidRuntime: FATAL EXCEPTION: Thread-314
Process: com.green.dao, PID: 4587
org.greenrobot.greendao.DaoException: Method may be called only in owner thread, use forCurrentThread to get an instance for this thread
at org.greenrobot.greendao.query.AbstractQuery.checkThread(AbstractQuery.java:99)
at org.greenrobot.greendao.query.Query.list(Query.java:87)
at com.green.dao.MainActivity$1.run(MainActivity.java:98)
at java.lang.Thread.run(Thread.java:818)
這個錯誤是在哪里拋出的呢?看下list()源碼
public List<T> list() {
checkThread();
Cursor cursor = dao.getDatabase().rawQuery(sql, parameters);
return daoAccess.loadAllAndCloseCursor(cursor);
}
protected void checkThread() {
if (Thread.currentThread() != ownerThread) {
throw new DaoException(//就是這里拋出了異常
"Method may be called only in owner thread, use forCurrentThread to get an instance for this thread");
}
}
啥意思碱屁?list()只能在它自己的線程中調(diào)用。 錯誤提示很清晰了蛾找,用 forCurrentThread 給當前線程一個 query
final Query<Country> query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
new Thread(new Runnable() {
@Override
public void run() {
List<Country> countries =query.forCurrentThread().list();
for (Country c:countries){
Log.e("返回",c.getName());
}
}
}).start();
這樣就ok 了娩脾。大概就是每個線程要綁定自己的query
所以下面這張寫法肯定沒有問題啦
new Thread(new Runnable() {
@Override
public void run() {
Query<Country> query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
List<Country> countries =query.list();
for (Country c:countries){
Log.e("返回",c.getName());
}
}
}).start();
Sql語句查詢
方式1
Query<City> query =EntityManager.getInstance().getCityDao().queryBuilder().where(new WhereCondition.StringCondition("_ID IN"+"(SELECT _ID FROM CITY WHERE _ID=1)")).build();
City city =query.unique();
StringCondition()中的內(nèi)容:_ID 可以換成任何字段,但是需要跟后面的查詢內(nèi)容保持一致打毛, 雖然后面的sql 語句是如果放到數(shù)據(jù)庫里查詢是只能返回id 但是這里不是 而是返回所有值柿赊。注意 IN 還有sql 語句的括號俩功。
方式2:
Query<City> query =EntityManager.getInstance().getCityDao().queryRawCreate("where _ID=?","1");
City city=query.unique();
Log.e("返回值",city.getPopulation()+"~~~"+city.getName()+"~~~"+city.getId());
方式3
List<City> citiess=EntityManager.getInstance().getCityDao().queryRaw("where _ID=?","1");
for (City city:citiess){
Log.e("返回值",city.getPopulation()+"~~~"+city.getName()+"~~~"+city.getId());
}