摘要:定義了一個(gè)這個(gè)標(biāo)記為源實(shí)體與另一個(gè)這個(gè)標(biāo)記為目標(biāo)實(shí)體的多個(gè)對(duì)象的關(guān)聯(lián)關(guān)系有一下三種方式來(lái)定義的映射關(guān)系。調(diào)試工具同時(shí)可以快速查看數(shù)據(jù)表結(jié)構(gòu)和數(shù)據(jù)。
大家好,在上一篇文章中,我主要介紹了GreenDao3.0的最基本的用法,當(dāng)然也是最常用的用法,如果你的項(xiàng)目里沒(méi)有特別復(fù)雜的多表關(guān)聯(lián)需求的話,我相信那篇文章的知識(shí)點(diǎn)已經(jīng)足夠使用了。但是,如果你是一個(gè)求知欲特別強(qiáng)的人或者手上有要在本地創(chuàng)建復(fù)雜的數(shù)據(jù)庫(kù)需求的話,我相信認(rèn)真讀完本篇文章,你一定會(huì)有所收獲。
好了廢話不多說(shuō),今天我們來(lái)學(xué)習(xí)下GreenDao的高級(jí)用法有哪些吧!閱讀本篇文章前你需要對(duì)GreenDao有一定的了解,如果你對(duì)GreenDao了解還不夠的話,建議先去閱讀史上最高效的ORM方案——GreenDao3.0詳解
目錄session 緩存
多表關(guān)聯(lián)
多表查詢
自定義參數(shù)類(lèi)型
與數(shù)據(jù)庫(kù)操作相關(guān)的AS插件
session 緩存如果你有多個(gè)相同的查詢語(yǔ)句去執(zhí)行,猜猜看返回給你的對(duì)象是一個(gè)還是多個(gè)?比如說(shuō)像下面這樣
QueryBuilderprojectQueryBuilder = projectDao .queryBuilder() .where(ProjectDao.Properties.UserName.eq("123456")); Query query = projectQueryBuilder.build(); Project project1=query.unique(); QueryBuilder projectQueryBuilder1 = projectDao .queryBuilder() .where(ProjectDao.Properties.UserName.eq("123456")); Query query2 = projectQueryBuilder1.build(); Project project2=query.unique();
答案是project1==project2而且project2查詢出來(lái)的速度要比project1查詢出來(lái)的速度快很多倍;
這是因?yàn)樵谕粋€(gè)session中如果一個(gè)entities已經(jīng)被session記錄那么下一次再次操作該實(shí)體時(shí),greenDao會(huì)先從內(nèi)存中查找,如果內(nèi)存中沒(méi)有再去數(shù)據(jù)庫(kù)中查找。這樣一方面就極大的提高greenDao的查詢效率,另一方面也是需要特別注意的是當(dāng)entities更新過(guò) greenDao仍然會(huì)從內(nèi)存中取出舊值,所以如果entities更新過(guò),需要去調(diào)用daoseesion.clear()方法清除緩存后才能查到最新值,否則查詢到的將還是保存在內(nèi)存中的值。
下面介紹下清除緩存有兩種方法
清除所所有的緩存
daoSession.clear();
清除指定Dao類(lèi)的緩存
projectDao = daoSession.getNoteDao(); projectDao.detachAll();多表關(guān)聯(lián)
1. 1:1關(guān)聯(lián)
當(dāng)我們?cè)谑褂胹qlite數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)表的1:1關(guān)聯(lián)時(shí),通常我們會(huì)在主表中定義一個(gè)外鍵去關(guān)聯(lián)副表,當(dāng)要查詢對(duì)應(yīng)的數(shù)據(jù)時(shí),首先我們要知道查詢數(shù)據(jù)的外鍵,然后需要用外鍵去副表中查詢所需要的數(shù)據(jù)。比如下面這樣
public class Customer { private Long id; } public class Order { private Long id; private Date date; private long customerId; }
Customer表通過(guò)id與Order表關(guān)聯(lián),查詢Order的Customer時(shí)需要先知道Order中的customerId然后根據(jù)id=customerId值再去數(shù)據(jù)庫(kù)中查詢?cè)搃d所對(duì)應(yīng)的Customer對(duì)象。然而在greenDao中一個(gè)注釋就可以搞定,只需要使用@ToOne注釋來(lái)定義一個(gè)關(guān)聯(lián)對(duì)象即可。
@ToOne 定義了一個(gè)entities與另一個(gè)entities的1:1對(duì)應(yīng)關(guān)系。通過(guò)joinProperty參數(shù)來(lái)定義一個(gè)外鍵下面是代碼示例
public class Order { @Id private Long id; private long customerId; @ToOne(joinProperty = "customerId") private Customer customer; } @Entity public class Customer { @Id private Long id; }
這樣只要獲得Order對(duì)象就能通過(guò)getCustomer()方法獲取Order所對(duì)應(yīng)的Customer了,這樣是不是很高效,很簡(jiǎn)便。其實(shí)getCustomer方法也很簡(jiǎn)單,就是在底層幫助我們封裝好了查詢語(yǔ)句而已,另外getCustomer獲取的對(duì)象也是懶查詢機(jī)制,只有真正使用getCustomer方法查詢到的對(duì)象時(shí)greenDao才會(huì)執(zhí)行查詢操作。如果你想立即執(zhí)行查詢操作可以調(diào)用DAO類(lèi)的loadDeep()與queryDeep()方法。
2. 1:N 關(guān)聯(lián)
在1對(duì)1關(guān)聯(lián)中每個(gè)顧客只能與一個(gè)訂單對(duì)應(yīng),但是現(xiàn)實(shí)生活中肯定不只是這樣,也會(huì)出現(xiàn)一個(gè)顧客下多個(gè)訂單的情況。如果出現(xiàn)這種需求的話,按照原生Sqlite的思路一樣是通過(guò)外鍵關(guān)聯(lián)即可,只是這一次查詢的對(duì)象會(huì)有很多個(gè),但是使用greenDao的1:1關(guān)聯(lián)方式顯然不行。不過(guò)別擔(dān)心greenDao還給我們準(zhǔn)備了@ToMany注釋。
@ToMany 定義了一個(gè)entities(這個(gè)標(biāo)記為源實(shí)體)與另一個(gè)entities(這個(gè)標(biāo)記為目標(biāo)實(shí)體)的多個(gè)對(duì)象的關(guān)聯(lián)關(guān)系:@Tomany有一下三種方式來(lái)定義1:N的映射關(guān)系。
referencedJoinProperty 在目標(biāo)實(shí)體中我們需要定義一個(gè)與源實(shí)體關(guān)聯(lián)起來(lái)的外鍵,即Order中的customerId,然后需要在源實(shí)體里我們需要將customerId作為referencedJoinProperty的屬性。說(shuō)起來(lái)很拗口,其實(shí)代碼很簡(jiǎn)單;
@Entity public class Customer { @Id private Long id; @ToMany(referencedJoinProperty = "customerId") @OrderBy("date ASC") private Listorders; } @Entity public class Order { @Id private Long id; private Date date; private long customerId; }
是不是很簡(jiǎn)單通過(guò)referencedJoinProperty來(lái)標(biāo)注下倆個(gè)實(shí)體之間的外鍵即可
joinProperties這個(gè)參數(shù)是referencedJoinProperty 參數(shù)的升級(jí)版。在referencedJoinProperty參數(shù)中我們發(fā)現(xiàn)倆個(gè)實(shí)體關(guān)聯(lián)的外鍵是CustomerId與id,但是如果我們的需求是外鍵不能通過(guò)id來(lái)定義,需要用自己自定義屬性來(lái)定義,第一種方法就沒(méi)法用了,而joinProperties就是為了解決這個(gè)需求的。
@Entity public class Customer { @Id private Long id; @Unique private String tag; @ToMany(joinProperties = { @JoinProperty(name = "tag", referencedName = "customerTag") }) @OrderBy("date ASC") private Listorders; } @Entity public class Order { @Id private Long id; private Date date; @NotNull private String customerTag; }
其實(shí)如果把
@ToMany(joinProperties = { @JoinProperty(name = "id", referencedName = "customerId") })
這樣的話就和第一種方法實(shí)現(xiàn)原理是一樣的了。
@JoinEntity 定義了N:M的映射關(guān)系。
@Entity public class Product { @Id private Long id; @ToMany @JoinEntity( entity = JoinProductsWithOrders.class, sourceProperty = "productId", targetProperty = "orderId" ) private ListordersWithThisProduct; } @Entity public class JoinProductsWithOrders { @Id private Long id; private Long productId; private Long orderId; } @Entity public class Order { @Id private Long id; }
3. 關(guān)聯(lián)表的更新與解析
關(guān)聯(lián)的查詢也是懶加載機(jī)制,而且查詢的結(jié)果會(huì)保存在緩存中下一次查詢的時(shí)候如果緩存有會(huì)直接從緩存中獲取結(jié)果。
同樣關(guān)聯(lián)表更新時(shí)因?yàn)橛芯彺鏅C(jī)制的存在你需要將改動(dòng)的表手動(dòng)的通過(guò)add()方法來(lái)更新關(guān)聯(lián)表中的對(duì)象或者直接清除緩存。
// 獲取當(dāng)前顧客的訂單列表 List多表查詢orders1 = customer.getOrders(); // 插入一個(gè)新訂單 Order order = new Order(); order.setCustomerId(customer.getId()); daoSession.insert(order); // 再一次獲取顧客的訂單 List orders2 = customer.getOrders(); // 因?yàn)榫彺媪斜頉](méi)有更新所以訂單1與訂單2的大小相等 assert(orders1.size() == orders2.size); // 也是相同的對(duì)象 assert(orders1.equals(orders2)); //調(diào)用該方法后,才能更新緩存列表 orders1.add(newOrder); //刪除時(shí)也許要手動(dòng)將緩存列表里面的數(shù)據(jù)刪除 List orders = customer.getOrders(); // 從數(shù)據(jù)庫(kù)中移除 daoSession.delete(someOrder); // 手動(dòng)從緩存列表移除 orders.remove(someOrder); //如果數(shù)據(jù)庫(kù)更新后不想手動(dòng)添加數(shù)據(jù)可以使用resetXX()方法來(lái)清除緩存 customer.resetOrders(); List orders = customer.getOrders();
有些時(shí)候我們的表沒(méi)有使用ToOne與ToMany建立關(guān)聯(lián)關(guān)系,但是我們又想一步到位。這時(shí)我們可以使用greenDao的多表查詢功能來(lái)幫助我們減少不必要的代碼。
1. 關(guān)聯(lián)單個(gè)表
//查詢地址是住在迪拜大樓的用戶 QueryBuilderqueryBuilder = userDao.queryBuilder(); queryBuilder.join(Address.class, AddressDao.Properties.userId) .where(AddressDao.Properties.Street.eq("迪拜大樓")); List users = queryBuilder.list();
通過(guò)queryBuilder.join()方法即可完成,其用法也很簡(jiǎn)單第一個(gè)參數(shù)是關(guān)聯(lián)的類(lèi),第二個(gè)是關(guān)聯(lián)類(lèi)中的關(guān)聯(lián)屬性。
2.關(guān)聯(lián)多個(gè)表
//查詢?cè)跉W洲人口超過(guò)100000的城市 QueryBuilder qb = cityDao.queryBuilder().where(Properties.Population.ge(1000000)); Join country = qb.join(Properties.CountryId, Country.class); Join continent = qb.join(country, CountryDao.Properties.ContinentId, Continent.class, ContinentDao.Properties.Id); continent.where(ContinentDao.Properties.Name.eq("Europe")); ListbigEuropeanCities = qb.list();
通過(guò)queryBuilder.join()鏈?zhǔn)秸{(diào)用來(lái)實(shí)現(xiàn)多表查詢
注意:多表查詢的前提是我們已經(jīng)定義好了外鍵來(lái)關(guān)聯(lián)表與表之間的關(guān)系。
默認(rèn)類(lèi)型參數(shù) :greenDao默認(rèn)支持的類(lèi)型參數(shù)如下
boolean, Boolean int, Integer short, Short long, Long float, Float double, Double byte, Byte byte[] String Date
自定義類(lèi)型參數(shù): 如果greenDao的默認(rèn)參數(shù)類(lèi)型滿足不了你的需求,比如你想定義一個(gè)顏色屬性,那么你可以使用數(shù)據(jù)庫(kù)支持的原生數(shù)據(jù)類(lèi)型通過(guò)PropertyConverter類(lèi)轉(zhuǎn)換成你想要的顏色屬性。
首先你需要給自定義類(lèi)型參數(shù)添加 @Convert注釋并添加對(duì)應(yīng)參數(shù)
converter:參數(shù)轉(zhuǎn)換類(lèi),columnType:在數(shù)據(jù)庫(kù)中對(duì)應(yīng)的類(lèi)型
實(shí)現(xiàn)PropertyConverter類(lèi)
下面是用通過(guò)使用數(shù)據(jù)庫(kù)支持的Integer類(lèi)型來(lái)轉(zhuǎn)換成數(shù)據(jù)庫(kù)不支持的枚舉類(lèi)型
@Entity public class User { @Id private Long id; @Convert(converter = RoleConverter.class, columnType = Integer.class) private Role role; public enum Role { DEFAULT(0), AUTHOR(1), ADMIN(2); final int id; Role(int id) { this.id = id; } } public static class RoleConverter implements PropertyConverter與數(shù)據(jù)庫(kù)相關(guān)的AS插件{ //將Integer值轉(zhuǎn)換成Role值 @Override public Role convertToEntityProperty(Integer databaseValue) { if (databaseValue == null) { return null; } for (Role role : Role.values()) { if (role.id == databaseValue) { return role; } } return Role.DEFAULT; } //將Role值轉(zhuǎn)換成Integer值 @Override public Integer convertToDatabaseValue(Role entityProperty) { return entityProperty == null ? null : entityProperty.id; } } }
快速清除數(shù)據(jù)庫(kù)本地?cái)?shù)據(jù)。ADB IDEA
調(diào)試工具同時(shí)可以快速查看數(shù)據(jù)表結(jié)構(gòu)和數(shù)據(jù)。 Stetho
感興趣的同學(xué)可以搜索下這倆個(gè)插件真的很好用。
后記上期有同學(xué)提問(wèn)greenDao的多線程同步機(jī)制,在這里我簡(jiǎn)單解釋下:
greenDao多線程同步可以通過(guò)forCurrentThread()來(lái)實(shí)現(xiàn)的,具體原理很簡(jiǎn)單我們看下源碼就知道了
//獲取當(dāng)前線程id long threadId = Thread.currentThread().getId(); //加鎖 synchronized (queriesForThreads) { //queryRef是一個(gè)Map集合 WeakReferencequeryRef = queriesForThreads.get(threadId); Q query = queryRef != null ? queryRef.get() : null; if (query == null) { gc(); query = createQuery(); //保存query queriesForThreads.put(threadId, new WeakReference(query)); } else { System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length); } return query; }
這是源碼的核心部分,從上面我們可以看出greenDao是通過(guò)將線程id與query對(duì)象存儲(chǔ)在Map集合中建立1:N的映射關(guān)系,不同線程只會(huì)取出屬于自己的query而不會(huì)調(diào)用其他線程的query。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70577.html
摘要:核心類(lèi)介紹是的入口也是頂級(jí)對(duì)象對(duì)于一個(gè)指定的表單持有數(shù)據(jù)庫(kù)對(duì)象數(shù)據(jù)庫(kù)并且能夠管理類(lèi)能夠創(chuàng)建表和刪除表其內(nèi)部類(lèi)與是創(chuàng)建數(shù)據(jù)庫(kù)的的具體實(shí)現(xiàn)對(duì)于一個(gè)指定的表單可以管理所有的對(duì)象。也能夠?qū)?shí)體類(lèi)執(zhí)行,操作。 1. 什么是greenDao 弄明白greenDao之前我們應(yīng)該先了解什么是ORM(Object Relation Mapping 即 對(duì)象關(guān)系映射),說(shuō)白了就是將面向?qū)ο缶幊陶Z(yǔ)言里的對(duì)象...
閱讀 2578·2021-11-22 09:34
閱讀 3559·2021-11-15 11:37
閱讀 2361·2021-09-13 10:37
閱讀 2120·2021-09-04 16:40
閱讀 1603·2021-09-02 15:40
閱讀 2472·2019-08-30 13:14
閱讀 3342·2019-08-29 13:42
閱讀 1917·2019-08-29 13:02