你們好,在上一篇文章中,我主要介紹了GreenDao3.0的最基本的用法,固然也是最經常使用的用法,若是你的項目裏沒有特別複雜的多表關聯需求的話,我相信那篇文章的知識點已經足夠使用了。可是,若是你是一個求知慾特別強的人或者手上有要在本地建立複雜的數據庫需求的話,我相信認真讀完本篇文章,你必定會有所收穫。sql
好了廢話很少說,今天咱們來學習下GreenDao的高級用法有哪些吧!閱讀本篇文章前你須要對GreenDao有必定的瞭解,若是你對GreenDao瞭解還不夠的話,建議先去閱讀史上最高效的ORM方案——GreenDao3.0詳解數據庫
若是你有多個相同的查詢語句去執行,猜猜看返回給你的對象是一個仍是多個?好比說像下面這樣segmentfault
QueryBuilder<Project> projectQueryBuilder = projectDao .queryBuilder() .where(ProjectDao.Properties.UserName.eq("123456")); Query<Project> query = projectQueryBuilder.build(); Project project1=query.unique(); QueryBuilder<Project> projectQueryBuilder1 = projectDao .queryBuilder() .where(ProjectDao.Properties.UserName.eq("123456")); Query<Project> query2 = projectQueryBuilder1.build(); Project project2=query.unique();
答案是project1==project2並且project2查詢出來的速度要比project1查詢出來的速度快不少倍;
這是由於在同一個session中若是一個entities已經被session記錄那麼下一次再次操做該實體時,greenDao會先從內存中查找,若是內存中沒有再去數據庫中查找。這樣一方面就極大的提升greenDao的查詢效率,另外一方面也是須要特別注意的是當entities更新過 greenDao仍然會從內存中取出舊值,因此若是entities更新過,須要去調用daoseesion.clear()方法清除緩存後才能查到最新值,不然查詢到的將仍是保存在內存中的值。
下面介紹下清除緩存有兩種方法緩存
清除所全部的緩存session
daoSession.clear();
清除指定Dao類的緩存多線程
projectDao = daoSession.getNoteDao(); projectDao.detachAll();
1. 1:1關聯
當咱們在使用sqlite數據庫來實現表的1:1關聯時,一般咱們會在主表中定義一個外鍵去關聯副表,當要查詢對應的數據時,首先咱們要知道查詢數據的外鍵,而後須要用外鍵去副表中查詢所須要的數據。好比下面這樣ide
public class Customer { private Long id; } public class Order { private Long id; private Date date; private long customerId; }
Customer表經過id與Order表關聯,查詢Order的Customer時須要先知道Order中的customerId而後根據id=customerId值再去數據庫中查詢該id所對應的Customer對象。然而在greenDao中一個註釋就能夠搞定,只須要使用@ToOne註釋來定義一個關聯對象便可。工具
@ToOne 定義了一個entities與另外一個entities的1:1對應關係。經過joinProperty參數來定義一個外鍵下面是代碼示例學習
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對象就能經過getCustomer()方法獲取Order所對應的Customer了,這樣是否是很高效,很簡便。其實getCustomer方法也很簡單,就是在底層幫助咱們封裝好了查詢語句而已,另外getCustomer獲取的對象也是懶查詢機制,只有真正使用getCustomer方法查詢到的對象時greenDao纔會執行查詢操做。若是你想當即執行查詢操做能夠調用DAO類的loadDeep()與queryDeep()方法。ui
2. 1:N 關聯
在1對1關聯中每一個顧客只能與一個訂單對應,可是現實生活中確定不僅是這樣,也會出現一個顧客下多個訂單的狀況。若是出現這種需求的話,按照原生Sqlite的思路同樣是經過外鍵關聯便可,只是這一次查詢的對象會有不少個,可是使用greenDao的1:1關聯方式顯然不行。不過別擔憂greenDao還給咱們準備了@ToMany註釋。
@ToMany 定義了一個entities(這個標記爲源實體)與另外一個entities(這個標記爲目標實體)的多個對象的關聯關係:@Tomany有一下三種方式來定義1:N的映射關係。
referencedJoinProperty 在目標實體中咱們須要定義一個與源實體關聯起來的外鍵,即Order中的customerId,而後須要在源實體裏咱們須要將customerId做爲referencedJoinProperty的屬性。提及來很拗口,其實代碼很簡單;
@Entity public class Customer { @Id private Long id; @ToMany(referencedJoinProperty = "customerId") @OrderBy("date ASC") private List<Order> orders; } @Entity public class Order { @Id private Long id; private Date date; private long customerId; }
是否是很簡單經過referencedJoinProperty來標註下倆個實體之間的外鍵便可
joinProperties這個參數是referencedJoinProperty 參數的升級版。在referencedJoinProperty參數中咱們發現倆個實體關聯的外鍵是CustomerId與id,可是若是咱們的需求是外鍵不能經過id來定義,須要用本身自定義屬性來定義,第一種方法就無法用了,而joinProperties就是爲了解決這個需求的。
@Entity public class Customer { @Id private Long id; @Unique private String tag; @ToMany(joinProperties = { @JoinProperty(name = "tag", referencedName = "customerTag") }) @OrderBy("date ASC") private List<Site> orders; } @Entity public class Order { @Id private Long id; private Date date; @NotNull private String customerTag; }
其實若是把
@ToMany(joinProperties = { @JoinProperty(name = "id", referencedName = "customerId") })
這樣的話就和第一種方法實現原理是同樣的了。
@JoinEntity 定義了N:M的映射關係。
@Entity public class Product { @Id private Long id; @ToMany @JoinEntity( entity = JoinProductsWithOrders.class, sourceProperty = "productId", targetProperty = "orderId" ) private List<Order> ordersWithThisProduct; } @Entity public class JoinProductsWithOrders { @Id private Long id; private Long productId; private Long orderId; } @Entity public class Order { @Id private Long id; }
3. 關聯表的更新與解析
關聯的查詢也是懶加載機制,並且查詢的結果會保存在緩存中下一次查詢的時候若是緩存有會直接從緩存中獲取結果。
一樣關聯表更新時由於有緩存機制的存在你須要將改動的表手動的經過add()方法來更新關聯表中的對象或者直接清除緩存。
// 獲取當前顧客的訂單列表 List<Order> orders1 = customer.getOrders(); // 插入一個新訂單 Order order = new Order(); order.setCustomerId(customer.getId()); daoSession.insert(order); // 再一次獲取顧客的訂單 List<Order> orders2 = customer.getOrders(); // 由於緩存列表沒有更新因此訂單1與訂單2的大小相等 assert(orders1.size() == orders2.size); // 也是相同的對象 assert(orders1.equals(orders2)); //調用該方法後,才能更新緩存列表 orders1.add(newOrder); //刪除時也許要手動將緩存列表裏面的數據刪除 List orders = customer.getOrders(); // 從數據庫中移除 daoSession.delete(someOrder); // 手動從緩存列表移除 orders.remove(someOrder); //若是數據庫更新後不想手動添加數據可使用resetXX()方法來清除緩存 customer.resetOrders(); List orders = customer.getOrders();
有些時候咱們的表沒有使用ToOne與ToMany創建關聯關係,可是咱們又想一步到位。這時咱們可使用greenDao的多表查詢功能來幫助咱們減小沒必要要的代碼。
1. 關聯單個表
//查詢地址是住在迪拜大樓的用戶 QueryBuilder<User> queryBuilder = userDao.queryBuilder(); queryBuilder.join(Address.class, AddressDao.Properties.userId) .where(AddressDao.Properties.Street.eq("迪拜大樓")); List<User> users = queryBuilder.list();
經過queryBuilder.join()方法便可完成,其用法也很簡單第一個參數是關聯的類,第二個是關聯類中的關聯屬性。
2.關聯多個表
//查詢在歐洲人口超過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")); List<City> bigEuropeanCities = qb.list();
經過queryBuilder.join()鏈式調用來實現多表查詢
注意:多表查詢的前提是咱們已經定義好了外鍵來關聯表與表之間的關係。
默認類型參數 :greenDao默認支持的類型參數以下
boolean, Boolean int, Integer short, Short long, Long float, Float double, Double byte, Byte byte[] String Date
自定義類型參數: 若是greenDao的默認參數類型知足不了你的需求,好比你想定義一個顏色屬性,那麼你可使用數據庫支持的原生數據類型經過PropertyConverter類轉換成你想要的顏色屬性。
實現PropertyConverter類
下面是用經過使用數據庫支持的Integer類型來轉換成數據庫不支持的枚舉類型
@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<Role, Integer> { //將Integer值轉換成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值轉換成Integer值 @Override public Integer convertToDatabaseValue(Role entityProperty) { return entityProperty == null ? null : entityProperty.id; } } }
感興趣的同窗能夠搜索下這倆個插件真的很好用。
上期有同窗提問greenDao的多線程同步機制,在這裏我簡單解釋下:
greenDao多線程同步能夠經過forCurrentThread()來實現的,具體原理很簡單咱們看下源碼就知道了
//獲取當前線程id long threadId = Thread.currentThread().getId(); //加鎖 synchronized (queriesForThreads) { //queryRef是一個Map集合 WeakReference<Q> queryRef = queriesForThreads.get(threadId); Q query = queryRef != null ? queryRef.get() : null; if (query == null) { gc(); query = createQuery(); //保存query queriesForThreads.put(threadId, new WeakReference<Q>(query)); } else { System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length); } return query; }
這是源碼的核心部分,從上面咱們能夠看出greenDao是經過將線程id與query對象存儲在Map集合中創建1:N的映射關係,不一樣線程只會取出屬於本身的query而不會調用其餘線程的query。