# 多表查询
# 对象导航查询
对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。例如:我们通过ID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。对象导航查询的使用要求是:两个对象之间必须存在关联关系。
查询一个客户,获取该客户下的所有联系人。
@Autowired
private CustomerDao customerDao;
@Test
//由于是在java代码中测试,为了解决no session问题,将操作配置到同一个事务中
@Transactional
public void testFind() {
Customer customer = customerDao.findOne(5l);
Set<LinkMan> linkMans = customer.getLinkMans();//对象导航查询
for(LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
}
查询一个联系人,获取该联系人的所有客户。
@Autowired
private LinkManDao linkManDao;
@Test
public void testFind() {
LinkMan linkMan = linkManDao.findOne(4l);
Customer customer = linkMan.getCustomer(); //对象导航查询
System.out.println(customer);
}
对象导航查询的问题分析:
问题1:我们查询客户时,要不要把联系人查询出来?
分析:如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的,不使用时又会白白的浪费了服务器内存。
解决:采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。
配置方式:
/**
* 在客户对象的@OneToMany注解中添加fetch属性
* FetchType.EAGER :立即加载
* FetchType.LAZY :延迟加载
*/
@OneToMany(mappedBy="customer",fetch=FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<>(0);
问题2:我们查询联系人时,要不要把客户查询出来?
分析:例如:查询联系人详情时,肯定会看看该联系人的所属客户。如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的话,一个对象不会消耗太多的内存。而且多数情况下我们都是要使用的。
解决: 采用立即加载的思想。通过配置的方式来设定,只要查询从表实体,就把主表实体对象同时查出来
配置方式
/**
* 在联系人对象的@ManyToOne注解中添加fetch属性
* FetchType.EAGER :立即加载
* FetchType.LAZY :延迟加载
*/
@ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER)
@JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id")
private Customer customer;
# 使用Specification查询
/**
* Specification的多表查询
*/
@Test
public void testFind() {
Specification<LinkMan> spec = new Specification<LinkMan>() {
public Predicate toPredicate(Root<LinkMan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//Join代表链接查询,通过root对象获取
//创建的过程中,第一个参数为关联对象的属性名称,第二个参数为连接查询的方式(left,inner,right)
//JoinType.LEFT : 左外连接,JoinType.INNER:内连接,JoinType.RIGHT:右外连接
Join<LinkMan, Customer> join = root.join("customer",JoinType.INNER);
return cb.like(join.get("custName").as(String.class),"传智播客1");
}
};
List<LinkMan> list = linkManDao.findAll(spec);
for (LinkMan linkMan : list) {
System.out.println(linkMan);
}
}
# Specification的toPredicate多条件查询
public Page<AnswerPaper> search(String searchKey, Pageable pageable) {
return answerpaperDao.findAll(new Specification<AnswerPaper>() {
private static final long serialVersionUID = 1L;
public Predicate toPredicate(Root<AnswerPaper> root,
CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> list = new ArrayList<Predicate>();
if (StringUtils.isNotBlank(searchKey)) {
list.add(builder.or(builder.like(root.get("namePinYin").as(String.class),
"%" + searchKey.toUpperCase() + "%"), builder.like(root.get("name").as(String.class),
"%" + searchKey + "%")));
}
Predicate[] p = new Predicate[list.size()];
return builder.and(list.toArray(p));
}
}, pageable);
}
public Predicate toPredicate(Root
其中 Root是查询结果的一个实体对象,也就是查询结果返回的主要对象,其中一对多OR多对一就是从这个对象开始计算的
然后是javax.persistence.criteria.CriteriaQuery<T>,这个是JPA标准,主要是构建查询条件的,里面的方法都是各种查询方式:distinct、select、where、groupby、having、orderby这些方法,想必大家都知道这些是组件SQL语句的基本方法。
接下来是javax.persistence.criteria.CriteriaBuilder,这个接口主要是用来进行一些函数操作,不过我们这里先关注JPA标准中Hibernate的两个实现方法:
1.and org.hibernate.ejb.criteria.CriteriaBuilderImpl.and(Predicate...)
2.or org.hibernate.ejb.criteria.CriteriaBuilderImpl.or(Predicate...)
这两个方法都有一个关键的接口:Predicate(javax.persistence.criteria.Predicate);
这个接口,同为Expression(javax.persistence.criteria.Expression
Root:查询哪个表 CriteriaQuery:查询哪些字段,排序是什么 CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式 Predicate(Expression):单独每一条查询条件的详细描述
Specification<User> specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<>();
// 1.等于 选择name是“张三”的用户 Predicate equal(Expression<?> var1, Object var2);
list.add(cb.equal(root.get("name").as(String.class), "张三"));
// 2.多表查询 选择teacher是“老师甲”
list.add(cb.equal(root.join("teacher").get("name").as(String.class), "老师甲"));
// 3.不包括集合 studentId不是7、8、9
List notInList = Arrays.asList(7,8,9);
list.add(cb.not(root.get("studentId").in(notInList)));
// 4.包括集合 studentId是4、5、6
List inList = Arrays.asList(4,5,6);
list.add(root.get("studentId").in(inList));
Predicate[] p = new Predicate[list.size()];
// cb.and “and”表示这几个条件并且的关系 Predicate and(Predicate... var1);
return cb.and(list.toArray(p));
}
};
Spring Data JPA支持JPA2.0的Criteria查询,相应的接口是JpaSpecificationExecutor。Criteria 查询:是一种类型安全和更面向对象的查询 。
这个接口基本是围绕着Specification接口来定义的, Specification接口中只定义了如下一个方法:
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
要理解这个方法,以及正确的使用它,就需要对JPA2.0的Criteria查询有一个足够的熟悉和理解,因为这个方法的参数和返回值都是JPA标准里面定义的对象。
关于评论
评论前请填好“昵称”、“邮箱”这两栏内容,否则不会收到回复,谢谢!