Introduction

在基本的数据查询实例中,可以通过实现CrudRepository接口来实现针对一个的查询或多个字段的组合查询,但这只是对于条件比较简单的情况下,如果条件比较复杂,那么一个方法的名字就会显的很长,那么就可以换一种方式来实现数据查询,比如下面即将提到的Criteria API, Specification, Query dsl.

Criteria API

CriteriaQuery

一个典型的查询代码如下:

LocalDate today = new LocalDate();

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
Root<Customer> root = query.from(Customer.class);

Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2); 
query.where(builder.and(hasBirthday, isLongTermCustomer));
em.createQuery(query.select(root)).getResultList();

主要步骤如下:

  1. 创建查询构造器CriteriaBuilder
  2. 实例化一个条件查询
  3. 创建一个查询的根元素
  4. 创建一个或多个查询的条件预测
  5. entityManager上执行查询
CriteriaUpdate/CriteriaDelete

CriteriaQuery是jpa 2.0引入的接口,而在jpa 2.1中还引入了CriteriaUpdateCriteriaDelete接口来实现对数据的修改和删除操作。
CriteriaUpdate的实现和CriteriaQuery类似,其实现如下:

CriteriaBuilder builder = entityManager
    .getCriteriaBuilder();

CriteriaUpdate<T> update = builder
    .createCriteriaUpdate(postModerateClass);

Root<T> root = update.from(postModerateClass);

Expression<Boolean> filterPredicate = builder
.like(
    builder.lower(root.get("message")), 
    "%spam%"
);

if(Post.class.isAssignableFrom(postModerateClass)) {
    filterPredicate = builder.or(
        filterPredicate, builder
        .like(
            builder.lower(root.get("title")), 
            "%spam%"
        )
    );
}

update
.set(root.get("status"), PostStatus.SPAM)
.set(root.get("updatedOn"), new Date())
.where(filterPredicate);

return entityManager
.createQuery(update)
.executeUpdate()

当然如果觉的写那么多模板代码比较麻烦,也可以直接通过EntityManagercreateQuery接口手写sql, 也是可以的。也有其他的方法,如下面的Specification接口。

Specification接口

Specification接口实现了可重用的预测(Predicate), 一个Specification接口就是一个查询条件,可以通过创建多个Specification接口实现复杂的条件查询,其接口如下:

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}

Root, CriteriaQuery, CriteriaBuilderCriteria中的一样,下面是一个示例:

public CustomerSpecifications {

  public static Specification<Customer> customerHasBirthday() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.equal(root.get(Customer_.birthday), today);
      }
    };
  }

  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
      }
    };
  }
}

为了让repository可以执行这个查询,需要repository接口实现JpaSpecificationExecutor接口。查询实例如下:

customerRepository.findAll(hasBirthday());

或者实现组合查询(jpa中提供了where, and, or 等帮助方法来简化操作):

customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));

Querydsl

开源项目Querydsl也提供了简化模板代码的实现, 你需要pom.xml中添加querydsl的包,然后添加插件以实现在每个源码包下自动创建查询类比如QCustomer

<plugin>
  <groupId>com.mysema.maven</groupId>
  <artifactId>maven-apt-plugin</artifactId>
  <version>1.0</version>
  <executions>
    <execution>
      <phase>generate-sources</phase>
      <goals>
        <goal>process</goal>
      </goals>
      <configuration>
        <outputDirectory>target/generated-sources</outputDirectory>
        <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
      </configuration>
    </execution>
  </executions>
</plugin>

查询代码如下:

BooleanExpression customerHasBirthday = customer.birthday.eq(today);
BooleanExpression isLongTermCustomer = customer.createdAt.lt(today.minusYears(2));
customerRepository.findAll(customerHasBirthday.and(isLongTermCustomer));

其中BooleanExpression和Specification类似,当然,repository需要实现相应的接口

public interface CustomerRepository extends JpaRepository<Customer>, QueryDslPredicateExecutor {
}

https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
https://vladmihalcea.com/jpa-criteria-api-bulk-update-delete/

posted on 2018-08-26 22:40  浮舟z  阅读(136)  评论(0编辑  收藏  举报