Spring Data JPA如何创建通用规范构建器

时间:2018-08-27 19:37:10

标签: java generics spring-data-jpa

我有不同的规范类别:

public class UserSpecification implements Specification<ApplicationUser> {
    private SearchCriteria criteria;

    public UserSpecification(SearchCriteria criteria) {
        this.criteria = criteria;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Predicate toPredicate(Root<ApplicationUser> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
       ...
    }
}

public class HotelSpecification implements Specification<Hotel> {

    private SearchCriteria criteria;

    public HotelSpecification(SearchCriteria criteria) {
        this.criteria = criteria;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Predicate toPredicate(Root<Hotel> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
      ...
    }
}

所以我尝试使用通用构建器来组成相同类型的规范,因为构建器类是99%重复的类,只是类类型不同。

public class MySpecificationBuilder {
    private final List<SearchCriteria> params;

    public MySpecificationBuilder () {
        params = new ArrayList<>();
    }

    public MySpecificationBuilder with(String key, String value) {
        params.add(new SearchCriteria(key, value));
        return this;
    }

    public Specification<?> build() {
        if (params.size() == 0) {
            return null;
        }

        List<Specification<?>> specs = new ArrayList<>();
        for (SearchCriteria param : params) {
            specs.add(new UserSpecification(param));  //how to make here generic
        }

        Specification<?> result = specs.get(0);
        for (int i = 1; i < specs.size(); i++) {
            result = Specification.where(result).and(specs.get(i)); //warning 1
        }
        return result;
    }
}

-警告1:enter image description here

我想知道是否鼓励/可以使用通用规范构建器。如果是这样,如何为不同的规格创建通用生成器?

2 个答案:

答案 0 :(得分:1)

如果我正确理解了您的目标,则应该可以执行以下操作。

public <T> Specification<T> build(Function<SearchCriteria, Specification<T>> mappingToSpecification) {
    if (params.size() == 0) {
        return null;
    }

    List<Specification<T>> specs = new ArrayList<>();
    for (SearchCriteria param : params) {
        specs.add(mappingToSpecification.apply(param));  //how to make here generic
    }

    Specification<T> result = specs.get(0);
    for (int i = 1; i < specs.size(); i++) {
        result = Specification.where(result).and(specs.get(i)); //warning 1
    }
    return result;
}

build方法具有类型参数,使您可以将其用于不同的类型,例如:

// Assumes builder.with has been called previously
builder.build(
    searchCriteria -> new MyObjectSpecification((SearchCriteria) searchCriteria)
);

答案 1 :(得分:0)

公共类GenericSpecificationBuilder {

private final List<SearchCriteria> params;

public GenericSpecificationBuilder() {
    params = new ArrayList<SearchCriteria>();
}

public GenericSpecificationBuilder with(String key, String operation, Object value,String perdicateType) {
    params.add(new SearchCriteria(key, operation, value, perdicateType));
    return this;
}

public Specification<T> build() {
    if (params.size() == 0) {
        return null;
    }

    List<Specification> specs = params.stream()
            .map(x -> getSpecification(x))
            .collect(Collectors.toList());

    Specification result = specs.get(0);

    for (int i = 1; i < params.size(); i++) {
        System.out.println(params.get(i)
                .isOrPredicate()
        );
        result = params.get(i-1)
                .isOrPredicate()
                ? Specification.where(result)
                .or(specs.get(i))
                : Specification.where(result)
                .and(specs.get(i));
    }
    return result;
}

public Specification<T> getSpecification(SearchCriteria criteria) {
    Specification<T> specification = new Specification<T>() {
        @Override
        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            Predicate predicate = genericCriteria(criteria,root,criteriaBuilder);
            return predicate;
        }
    };
    return specification;
}


public Predicate genericCriteria(SearchCriteria criteria, Root<?> root, CriteriaBuilder builder){

    if (criteria.getOperation().equalsIgnoreCase(">")) {
        return builder.greaterThanOrEqualTo(
                root.<String> get(criteria.getKey()), criteria.getValue().toString());
    }
    else if (criteria.getOperation().equalsIgnoreCase("<")) {
        return builder.lessThanOrEqualTo(
                root.<String> get(criteria.getKey()), criteria.getValue().toString());
    }
    else if (criteria.getOperation().equalsIgnoreCase(":")) {
        if (root.get(criteria.getKey()).getJavaType() == String.class) {
            return builder.like(
                    root.<String>get(criteria.getKey()), "%" + criteria.getValue() + "%");
        } else {
            return builder.equal(root.get(criteria.getKey()), criteria.getValue());
        }
    }
    return null;
}

}

这样,除非您想限制某个实体的某些操作,否则不必为每个实体创建Specification的单独实现。