# JpaAssist JPA工具

MornBoot提供JPA相关辅助功能,以简化JPA标准查询的开发工作。JPA标准查询封装度很高,有利于封装和重用,但代码冗长,可读性较低,而JPA Assist使JPA变得简洁、高效。

Since:v1.0.0

SpringDataJPA标准查询围绕关键接口JpaSpecificationExecutor<T>Specification<T>进行,主要构建Specification<T>对象后,调用JpaSpecificationExecutor<T>相关查询接口完成。JPA Assist的主要职责就是极简方式构建Specification<T>对象。

# 必要配置

Maven

<!--Spring Boot Data JPA-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
  <version>${boot.version}</version>
</dependency>
<!--Morn JPA Assist-->
<dependency>
  <groupId>site.morn.boot</groupId>
  <artifactId>morn-boot-jpa</artifactId>
  <version>${morn.version}</version>
</dependency>

# Test Entity & Repository

@Data
@Entity
public class TestUser {

  @Id
  @GeneratedValue
  private Long id;

  @Column
  private String username;

  @Column
  private String password;
}

public interface TestUserRepository extends CrudRepository<TestUser, Long>,
    JpaSpecificationExecutor<TestUser> {

}

# Model Query

基于实体模型获取属性和值,生成查询条件

# Test User Data

{
  "id": 1,
  "username": "timely-rain",
  "password": null
}

# Equal All

JpaBatchCondition#equalAll()会检索实体类中的所有属性和值,并生成两者的equal条件。若无法属性/值中的一者,则忽略该属性。

@Autowired
private TestUserRepository repository;

private List<TestUser> equalAll(TestUser user) {
  // 构建查询条件
  SpecificationFunction specificationFunction = (reference, restrain, condition) -> {

    // WHERE id = 1 AND username = 'timely-rain'
    // password为空,所以忽略
    Predicate[] equalAll = condition.equalAll();

    restrain.appendAnd(equalAll);
  };
  Specification<TestUser> specification = SpecificationBuilder.withParameter(user)
      .specification(specificationFunction);
  return repository.findAll(specification);
}

# Data Query

很多情况下,Entity无法携带特殊的数据格式,而@Transient会让实体类变得冗长。因此,JPA Assist提供Map的传参形式。

# Test Attach Data

{
  "keywords": "timely"
}

# Contain

JpaCondition#contain(String name, String valueName)对应SQL的LIKE %something%name引用实体类属性,而valueName引用model中的值,若model中不存在该值时,则从attach中获取。

@Autowired
private TestUserRepository repository;

private List<TestUser> contains(TestUser user, CriteriaMap attach) {
  SpecificationFunction specificationFunction = (reference, restrain, condition) -> {

    // WHERE id = 1
    // password为空,所以忽略
    Predicate[] equals = condition.equals("id", "password");

    // AND username LIKE '%timely%'
    Predicate keywords = condition.contain("username", "keywords");

    restrain.appendAnd(restrain.mergeAnd(equals), keywords);
  };
  Specification<TestUser> specification = SpecificationBuilder.withParameter(user, attach)
      .specification(specificationFunction);
  return repository.findAll(specification);
}

# JPA Query

JPA Assist目前的API并不多,有些时候仍需要使用JPA原生API。对比Model Query,这里的代码明显多了不少。

public void equals() {
  // 构建查询条件
  SpecificationFunction specificationFunction = (reference, restrain, condition) -> {

    Root root = reference.root();
    CriteriaBuilder builder = reference.builder();

    // WHERE id = 1
    if (Objects.nonNull(user.getId())) {
      Predicate id = builder.equal(root.get("id"), user.getId());
      restrain.appendAnd(id);
    }

    // AND username = 'timely-rain'
    if (!StringUtils.isEmpty(user.getUsername())) {
      Predicate username = builder.equal(root.get("username"), user.getUsername());
      restrain.appendAnd(username);
    }
  };
  Specification<TestUser> specification = SpecificationBuilder.withParameter(user)
      .specification(specificationFunction);
  repository.findAll(specification);
}

# API Condition

/**
 * 等于
 *
 * @param name 实体属性名称,数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#equal(Expression, Object)
 */
Predicate equal(String name);

/**
 * 等于
 *
 * @param name 实体属性名称
 * @param valueName 数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#equal(Expression, Object)
 */
Predicate equal(String name, String valueName);

/**
 * 不等
 *
 * @param name 实体属性名称,数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#notEqual(Expression, Object)
 */
Predicate notEqual(String name);

/**
 * 不等
 *
 * @param name 实体属性名称
 * @param valueName 数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#notEqual(Expression, Object)
 */
Predicate notEqual(String name, String valueName);

/**
 * 在...内
 *
 * @param name 实体属性名称,数据属性名称
 * @return 查询断言
 * @see Expression#in(Object...)
 */
Predicate in(String name);

/**
 * 包含
 *
 * @param name 实体属性名称,数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#like(Expression, String)
 */
Predicate contain(String name);

/**
 * 包含
 *
 * @param name 实体属性名称
 * @param valueName 数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#like(Expression, String)
 */
Predicate contain(String name, String valueName);

/**
 * 以...开始
 *
 * @param name 实体属性名称,数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#like(Expression, String)
 */
Predicate startWith(String name);

/**
 * 以...结束
 *
 * @param name 实体属性名称,数据属性名称
 * @return 查询断言
 * @see javax.persistence.criteria.CriteriaBuilder#like(Expression, String)
 */
Predicate endWith(String name);