标准 API 加入过滤器
Criteria API join with filter
所以我试图用 JPA 标准 Api 替换以下原生 SQL:
select CAT.* from CAT
join OWNER.ID = CAT.OWNER_ID
where OWNER.NAME = :ownerName
或
select CAT.* from CAT, OWNER
where OWNER.ID = CAT.OWNER_ID
and OWNER.NAME = :ownerName
实体看起来有点像这样:
class Owner {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@Column(name = "NAME", length = 15)
private String name;
...
}
class Cat {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@Column(name = "OWNER_ID", nullable = false)
private Long ownerId;
@Column(name = "NAME", length = 15)
private String name;
...
}
我实现了一个 org.springframework.data.jpa.domain.Specification
我可以与 JpaSpecificationExecutor 一起使用,例如:
@Primary
@Repository
public interface CatRepository
extends JpaRepository<Cat, Long>, JpaSpecificationExecutor<Cat> {}
和
interface Function3<ARG1, ARG2, ARG3, RETURN> {
RETURN apply(ARG1 arg1, ARG2 arg2, ARG3 arg3);
}
public static <TYPE> Specification<TYPE> create(
final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate) {
return (root, query, criteriaBuilder) -> {
return predicate.apply(root, (AbstractQuery<TYPE>) query, criteriaBuilder);
};
}
public static <TYPE, JOINTYPE>
Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(
Class<JOINTYPE> joinClass,
String joinColumn,
String joiningColumn,
final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate,
final Function3<Root<JOINTYPE>, AbstractQuery<JOINTYPE>, CriteriaBuilder, Predicate>
joinPredicate) {
return (root, criteriaQuery, criteriaBuilder) -> {
CriteriaQuery<JOINTYPE> joinQuery = criteriaBuilder.createQuery(joinClass);
Root<JOINTYPE> joinRoot = joinQuery.from(joinClass);
//TODO add a filter here for Owner.name here
// joinQuery.where(joinPredicate.apply(joinRoot, joinQuery, criteriaBuilder));
return criteriaBuilder.equal(root.get(joinColumn), joinRoot.get(joiningColumn));
};
}
但我只得到 join/select 的一侧,例如:
select
generatedAlias0
from
com.some.pckg.Cat as generatedAlias0
where
generatedAlias0.ownerId=generatedAlias1.id
当我这样做时:
catRepo.find(create(join(Owner.class,"ownerId","id", null, null)));
如何在此处将 table 添加到 select?所以最终生成的值看起来像:
select
generatedAlias0
from
com.some.pckg.Cat as generatedAlias0,
com.some.pckg.Owner as generatedAlias1
where
generatedAlias0.ownerId=generatedAlias1.id
问题是您正在规范中创建新查询。
您需要扩展一个已传递到规范中的现有的。
1.未指定实体之间的关系
static <TYPE, JOINTYPE> Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(Class<JOINTYPE> joinClass, String joinColumn, String joiningColumn) {
return (root, criteriaQuery, criteriaBuilder) -> {
Root<JOINTYPE> joinRoot = criteriaQuery.from(joinClass);
return criteriaBuilder.equal(root.get(joinColumn), joinRoot.get(joiningColumn));
};
2。 @ManyToOne
关系已定义
@Entity
public class Cat {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@ManyToOne
@JoinColumn(name = "OWNER_ID", nullable = false)
private Owner owner;
@Column(name = "NAME", length = 15)
private String name;
}
static <TYPE, JOINTYPE> Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(Class<JOINTYPE> joinClass, String relationPropery) {
return (root, criteriaQuery, criteriaBuilder) -> {
root.join(relationPropery);
return criteriaBuilder.conjunction();
};
}
完全实施:
@Repository
public interface CatSpecificationRepository extends JpaRepository<Cat, Long>, JpaSpecificationExecutor<Cat> {
static <TYPE> Specification<TYPE> create(final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate) {
return (root, query, criteriaBuilder) -> predicate.apply(root, (AbstractQuery<TYPE>) query, criteriaBuilder);
}
static <TYPE, JOINTYPE> Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(
Class<JOINTYPE> joinClass,
String joinColumn,
String joiningColumn,
final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate,
final Function3<Root<JOINTYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> joinPredicate) {
return (root, criteriaQuery, criteriaBuilder) -> {
Root<JOINTYPE> joinRoot = criteriaQuery.from(joinClass);
List<Predicate> predicates = new LinkedList<>();
predicates.add(criteriaBuilder.equal(root.get(joinColumn), joinRoot.get(joiningColumn)));
if (joinPredicate != null) {
predicates.add(joinPredicate.apply(joinRoot, criteriaQuery, criteriaBuilder));
}
if (predicate != null) {
predicates.add(predicate.apply(root, criteriaQuery, criteriaBuilder));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
static <TYPE, JOINTYPE> Function3<Root<JOINTYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate>isNameEquals(String value) {
return value != null ? (root, query, builder) -> builder.equal(root.get("name"), value) : null;
}
}
用法示例
catSpecificationRepository.findAll(create(join(Owner.class,"ownerId","id", null, isNameEquals(value))));
生成的查询
select
cat0_.id as id1_2_,
cat0_.name as name2_2_,
cat0_.owner_id as owner_id3_2_
from
cat cat0_ cross
join
owner owner1_
where
cat0_.owner_id=owner1_.id
and owner1_.name=?
请注意, 当前 SQL 查询构造:
from
cat cross join owner
where
cat.owner_id=owner.id
相当于
from
cat inner join owner on (cat.owner_id=owner.id )
或
from
cat, owner
where
cat.owner_id=owner.id
所以我试图用 JPA 标准 Api 替换以下原生 SQL:
select CAT.* from CAT
join OWNER.ID = CAT.OWNER_ID
where OWNER.NAME = :ownerName
或
select CAT.* from CAT, OWNER
where OWNER.ID = CAT.OWNER_ID
and OWNER.NAME = :ownerName
实体看起来有点像这样:
class Owner {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@Column(name = "NAME", length = 15)
private String name;
...
}
class Cat {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@Column(name = "OWNER_ID", nullable = false)
private Long ownerId;
@Column(name = "NAME", length = 15)
private String name;
...
}
我实现了一个 org.springframework.data.jpa.domain.Specification
我可以与 JpaSpecificationExecutor 一起使用,例如:
@Primary
@Repository
public interface CatRepository
extends JpaRepository<Cat, Long>, JpaSpecificationExecutor<Cat> {}
和
interface Function3<ARG1, ARG2, ARG3, RETURN> {
RETURN apply(ARG1 arg1, ARG2 arg2, ARG3 arg3);
}
public static <TYPE> Specification<TYPE> create(
final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate) {
return (root, query, criteriaBuilder) -> {
return predicate.apply(root, (AbstractQuery<TYPE>) query, criteriaBuilder);
};
}
public static <TYPE, JOINTYPE>
Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(
Class<JOINTYPE> joinClass,
String joinColumn,
String joiningColumn,
final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate,
final Function3<Root<JOINTYPE>, AbstractQuery<JOINTYPE>, CriteriaBuilder, Predicate>
joinPredicate) {
return (root, criteriaQuery, criteriaBuilder) -> {
CriteriaQuery<JOINTYPE> joinQuery = criteriaBuilder.createQuery(joinClass);
Root<JOINTYPE> joinRoot = joinQuery.from(joinClass);
//TODO add a filter here for Owner.name here
// joinQuery.where(joinPredicate.apply(joinRoot, joinQuery, criteriaBuilder));
return criteriaBuilder.equal(root.get(joinColumn), joinRoot.get(joiningColumn));
};
}
但我只得到 join/select 的一侧,例如:
select
generatedAlias0
from
com.some.pckg.Cat as generatedAlias0
where
generatedAlias0.ownerId=generatedAlias1.id
当我这样做时:
catRepo.find(create(join(Owner.class,"ownerId","id", null, null)));
如何在此处将 table 添加到 select?所以最终生成的值看起来像:
select
generatedAlias0
from
com.some.pckg.Cat as generatedAlias0,
com.some.pckg.Owner as generatedAlias1
where
generatedAlias0.ownerId=generatedAlias1.id
问题是您正在规范中创建新查询。
您需要扩展一个已传递到规范中的现有的。
1.未指定实体之间的关系
static <TYPE, JOINTYPE> Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(Class<JOINTYPE> joinClass, String joinColumn, String joiningColumn) {
return (root, criteriaQuery, criteriaBuilder) -> {
Root<JOINTYPE> joinRoot = criteriaQuery.from(joinClass);
return criteriaBuilder.equal(root.get(joinColumn), joinRoot.get(joiningColumn));
};
2。 @ManyToOne
关系已定义
@Entity
public class Cat {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Long id;
@ManyToOne
@JoinColumn(name = "OWNER_ID", nullable = false)
private Owner owner;
@Column(name = "NAME", length = 15)
private String name;
}
static <TYPE, JOINTYPE> Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(Class<JOINTYPE> joinClass, String relationPropery) {
return (root, criteriaQuery, criteriaBuilder) -> {
root.join(relationPropery);
return criteriaBuilder.conjunction();
};
}
完全实施:
@Repository
public interface CatSpecificationRepository extends JpaRepository<Cat, Long>, JpaSpecificationExecutor<Cat> {
static <TYPE> Specification<TYPE> create(final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate) {
return (root, query, criteriaBuilder) -> predicate.apply(root, (AbstractQuery<TYPE>) query, criteriaBuilder);
}
static <TYPE, JOINTYPE> Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> join(
Class<JOINTYPE> joinClass,
String joinColumn,
String joiningColumn,
final Function3<Root<TYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> predicate,
final Function3<Root<JOINTYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate> joinPredicate) {
return (root, criteriaQuery, criteriaBuilder) -> {
Root<JOINTYPE> joinRoot = criteriaQuery.from(joinClass);
List<Predicate> predicates = new LinkedList<>();
predicates.add(criteriaBuilder.equal(root.get(joinColumn), joinRoot.get(joiningColumn)));
if (joinPredicate != null) {
predicates.add(joinPredicate.apply(joinRoot, criteriaQuery, criteriaBuilder));
}
if (predicate != null) {
predicates.add(predicate.apply(root, criteriaQuery, criteriaBuilder));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
static <TYPE, JOINTYPE> Function3<Root<JOINTYPE>, AbstractQuery<TYPE>, CriteriaBuilder, Predicate>isNameEquals(String value) {
return value != null ? (root, query, builder) -> builder.equal(root.get("name"), value) : null;
}
}
用法示例
catSpecificationRepository.findAll(create(join(Owner.class,"ownerId","id", null, isNameEquals(value))));
生成的查询
select
cat0_.id as id1_2_,
cat0_.name as name2_2_,
cat0_.owner_id as owner_id3_2_
from
cat cat0_ cross
join
owner owner1_
where
cat0_.owner_id=owner1_.id
and owner1_.name=?
请注意, 当前 SQL 查询构造:
from
cat cross join owner
where
cat.owner_id=owner.id
相当于
from
cat inner join owner on (cat.owner_id=owner.id )
或
from
cat, owner
where
cat.owner_id=owner.id