在关联实体上重用JPA"规范"



假设我已经用这种方式编写了一个可重用JPA规范的小型,作为更多的语法糖

public static Specification<Person> lastNameEquas(String lastName) {
return (r,q,cb)->cb.equals(r.get(Person_.lastName),lastName);
}

想象更多的谓词,我使用它们,例如:

Specification<Person> s = where(firstNameEquals("John"))
.and(lastNameEquals("Smith"))
.and(ageGreaterThan(18));

TU联接实体的情况下,我也面临着为T实体重用定义的Specification<T>的问题

假设类Person@OneToMany-连接到Pet,并且我有PersonPet的规范,我希望在规范的相同构造中重用PersonPet的助手方法


@Entity
public class Person{
......
}
@Entity
public class Pet{
private String name;
private int age;
@ManyToOne
Person owner
}

我想要一个可以与可重复使用的Specification<Person>实例组合的Specification<Pet>


Specification<Pet> spec = where(PetSpecs.petNameEqual("Lucky"))
.and(PetSpecs.petAgeGreaterThan(1))
.and(PetSpecs.owner(
personNameEquals("John")
.and(personAgeGreaterThan(18))
))
select from Pet p where
p.name = 'Lucky' and p.age > 1 and p.owner.name = 'John' and p.owner.age > 18;

到目前为止我尝试了什么

我想写一个方法public static Specification<Pet> owner(Specification<Person>),它接受任何Specification-of-Person的输入,并将其应用于联接的属性,从而生成一个可以提供查询的Specification-of-Pet

更一般地说,我可以尝试写

public static <T, U> Specification<T> joinedSpecification(@NonNull SingularAttribute<T, U> joinAttribute, JoinType joinType, Specification<U> joinSpecification) {
if (joinSpecification == null) return (root, query, criteriaBuilder) -> null;
return (root, query, criteriaBuilder) -> {
Join<T, U> join = root.join(joinAttribute, joinType);
return joinSpecification.toPredicate(join, query, criteriaBuilder);
};
}

其思想是,Specification是一个返回谓词的函数,因此我的谓词将递归地将输入规范转换为更多的谓词,应用于连接的实体。

现在是我的问题。JPA将Specification<T>定义为lambda接口

Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);

注意,Root<X>扩展From<X,X>扩展Path<X>Join<T,U>扩展Path<T,U>

上面的代码不编译,因为root.join(...)返回一个JoinPath(获取实体属性),但不是Root

我的问题是:在JPA中,是否有可能重用规范来在联接路径中重新应用?

定义自己的接口似乎没什么大不了的:

@FunctionalInterface
public interface Specification<X> extends Serializable {
Predicate toPredicate(From<?, ? extends X> from, CriteriaQuery<?> cq, CriteriaBuilder cb);

default Specification<X> not() {
return (from, cq, cb) -> cb.not(toPredicate(from, cq, cb));
}
default Specification<X> and(@Nullable Specification<? super X> other) {
if (other == null) {
return this;
}
return (path, cq, cb) -> cb.and(toPredicate(path, cq, cb), other.toPredicate(path, cq, cb));
}
default Specification<X> or(@Nullable Specification<? super X> other) {
if (other == null) {
return this;
}
return (path, cq, cb) -> cb.or(toPredicate(path, cq, cb), other.toPredicate(path, cq, cb));
}
default org.springframework.data.jpa.domain.Specification<X> asSpring() {
return this::toPredicate;
}
}

然而,我们发现重用标准谓词比spring规范更方便(至少大多数开发人员更喜欢这个选项),smth。类似:

@Entity
public class Pet {
private String name;

private int age;
public static Predicate hasName(From<?, ? extends Pet> from, CriteriaBuilder cb, String name) {
return cb.equal(from.get(Pet_.name), cb.literal(name));
}
@ManyToOne
Person owner;
}

最新更新