你们好,在上一篇文章中,我主要介绍了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。