spring-data-jpa所有的语法规定如下图:
JPA标准对象理解
官方解释:
* Root<T> root:代表了可以查询和操作的实体对象的根,
* 可以通过它的 Path<Y> get(String attributeName); 这个方法拿到我们要操作的字段
* 注意:只可以拿到对应的T的字段(Employee)
* CriteriaQuery<?> query:代表一个specific的顶层查询对象
* 包含查询的各个部分,比如select,from,where,group by ,order by 等
* 简单理解 就是它提供 了查询ROOT的方法(where,select,having)
* CriteriaBuilder cb:用来构建CriteriaQuery的构建器对象(相当于条件或者说条件组合)
* 构造好后以Predicate的形式返回
* PageRequest代表分页信息,PageRequest里面的Sort对象是排序信息
*/
/**
* 非官方理解:
* 查询的时候就需要给一个标准(规范)
* -》 根据规范(这个规范我们可以先简单理解为查询的条件)进行查询
*
* Root:查询哪个表(定位到表和字段-> 用于拿到表中的字段)
* 可以查询和操作的实体的根
* Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似
* Root<Employee> 相当于 from Employee
* Root<Product> 相当于 from Product
* CriteriaQuery:查询哪些字段,排序是什么(主要是把多个查询的条件连系起来)
* CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
* 主要判断关系(和这个字段是相等,大于,小于like等)
* Predicate(Expression):单独每一条查询条件的详细描述 整个 where xxx=xx and yyy=yy ...
*/
CriteriaBuilder对象
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Student> query = builder.createQuery(Student.class);
CriteriaQuery对象
Root<Student> root = query.from(Student.class);
接下来
Predicate p1 = builder.like(root.<String> get("name"), "%" + student.getName() + "%");
Predicate p2 = builder.equal(root.<String> get("password"), student.getPassword());
query.where(p1, p2);
大致过程创建builder => 创建Query => 构造条件 => 查询
Spring jpa data封装
/**
* Applies the given {@link Specification} to the given {@link CriteriaQuery}.
*
* @param spec can be {@literal null}.
* @param query must not be {@literal null}.
* @return
*/
private <S> Root<T> applySpecificationToCriteria(Specification<T> spec, CriteriaQuery<S> query) {
Assert.notNull(query);
Root<T> root = query.from(getDomainClass());
if (spec == null) {
return root;
}
CriteriaBuilder builder = em.getCriteriaBuilder();
Predicate predicate = spec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
}
return root;
}
In查询
if(!CollectionUtils.isEmpty(ids)) {
In<Long> in = cb.in(root.<Long> get("id"));
for (Long id : ids) {
in.value(id);
}
query.where(in);
}
@Lock可以使用悲观锁
@Version使用乐观锁
Join查询
@Override
public List<Employee> findByCompanyName(final String companyName) {
List<Employee> employeeList = employeeRepository.findAll(new Specification<Employee>() {
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// ListJoin<Employee, Company> companyJoin = root.join(root.getModel().getList("companyList", //Company.class), JoinType.LEFT);
//Join对象与Root对象分别指代Company,Employee对象
Join<Employee, Company> companyJoin = root.join("companyList", JoinType.LEFT);
return cb.equal(companyJoin.get("name"), companyName);
}
});
return employeeList;
}
需要多个对象进行Join
@Override
public List<Employee> findByCompanyName(final String companyName, final String wage) {
List<Employee> employeeList = employeeRepository.findAll(new Specification<Employee>() {
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// ListJoin<Employee, Company> companyJoin = root.join(root.getModel().getList("companyList", //Company.class), JoinType.LEFT);
Join<Employee, Company> companyJoin = root.join("companySet", JoinType.LEFT);
Join<Employee, Wage> wageJoin = root.join("wageSet", JoinType.LEFT);
Predicate p1 = cb.equal(companyJoin.get("name"), companyName);
Predicate p2 = cb.equal(wageJoin.get("name"), wage);
// return cb.and(p1, p2);根据spring-data-jpa的源码,可以返回一个Predicate,框架内部会自动做query.where(p)的操作,也可以直接在这里处理,然后返回null,/// 也就是下面一段源码中的实现
query.where(p1, p2);
return null;
}
});
return employeeList;
}
虽然说JPA中这种方式查询会存在着多次级联查询的问题,对性能有所影响,但是在一般的企业级应用当中,为了开发的便捷,这种性能牺牲一般来说是可以接受的。
特别的:在一对多中或者多对一中,即便是fetch为eager,也会先查询主对象,再查询关联对象,但是在eager的情况下虽然是有多次查询问题,但是没有n+1问题,关联对象不会像n+1那样多查询n次,而仅仅是把关联对象一次性查询出来,因此,在企业级应用当中,访问量不大的情况下一般来说没什么问题。