如何有条件地添加JPA规范进行查询?
How to add JPA specifications conditionally to query?
我正在尝试在 JPA 中的 sqlfiddle 上编写此处概述的以下查询。我首先尝试将 @query
注释与 native = true
一起使用并且确实有效,但我的问题是我希望查询更加动态,因为可能是我不想添加的情况按名称或帐户过滤的子句。
我的实体看起来像这样:
@Entity
@Table(name = "INSTRUCTION")
public class Instruction {
@Id
@Column(name = "ID", nullable = false, unique = true)
public Long id;
@Column(name = "ACCOUNT", nullable = false)
public String account;
@Column(name = "NAME", nullable = false)
public String name;
@OneToMany(cascade = CascadeType.All, fetch = FetchType.EAGER)
@JoinColumn(name = "INSTRUCTION_ID", referenceColumnName = "ID")
@OrderBy("lastUpdated")
private List<Audit> auditItems = new ArrayList<>();
//Getters & Setters
}
.
@Entity
@Table(name = "AUDIT")
public class Audit {
@Id
@Column(name = "ID", nullable = false, unique = true)
public Long id;
@Column(name = "INSTRUCTION_STATUS", nullable = false)
public InstructionStatus status;
@Column(name = "LAST_UPDATED", nullable = false)
public LocalDateTime lastUpdated;
@Column(name = "LAST_UPDATED_BY", nullable = false)
public String lastUpdatedBy;
//Getters & Setters
}
我研究过使用规范来执行此操作,并且我设法将查询分解为不同的规范,如下所示:
private Specification<Instruction> hasAccount(String account) {
return (root, query, criteriaBuilder) -> criteriaBuilder.in(root.get("account")).value(account);
}
private Specification<Instruction> havingStatus(List<String> status) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
final Subquery<Audit> auditSubquery = query.subquery(Audit.class);
final Root<Audit> audit = auditSubquery.from(Audit.class);
//select instruction id from audit where status is not in {status}
auditSubquery.select(audit.get("instruction").get("id"));
auditSubquery.where(criteriaBuilder.trim(audit.get("status")).in(status).not());
//Select instruction from table where
predicates.add(root.get("id").in(auditSubquery).not());
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
};
}
// Other specifications....
像这样调用时它们工作正常:
final List<Instruction> instructions = this.instructionRepository.findAll(
where(havingStatus(statuses)
.and(hasAccount(account))));
但我的目标是拥有它,例如我可以检查 if account == null
然后不包括 hasAccount
规范,对于其他可能为空的字段依此类推。我有办法做到这一点吗?
这应该可以解决问题。
Specification spec = where(null);
if (statuses != null) {
spec = spec.and(havingStatus(statuses))
}
if (account != null) {
spec = spec.and(hasAccount(account))
}
final List<Instruction> instructions = this.instructionRepository.findAll(spec);
我正在尝试在 JPA 中的 sqlfiddle 上编写此处概述的以下查询。我首先尝试将 @query
注释与 native = true
一起使用并且确实有效,但我的问题是我希望查询更加动态,因为可能是我不想添加的情况按名称或帐户过滤的子句。
我的实体看起来像这样:
@Entity
@Table(name = "INSTRUCTION")
public class Instruction {
@Id
@Column(name = "ID", nullable = false, unique = true)
public Long id;
@Column(name = "ACCOUNT", nullable = false)
public String account;
@Column(name = "NAME", nullable = false)
public String name;
@OneToMany(cascade = CascadeType.All, fetch = FetchType.EAGER)
@JoinColumn(name = "INSTRUCTION_ID", referenceColumnName = "ID")
@OrderBy("lastUpdated")
private List<Audit> auditItems = new ArrayList<>();
//Getters & Setters
}
.
@Entity
@Table(name = "AUDIT")
public class Audit {
@Id
@Column(name = "ID", nullable = false, unique = true)
public Long id;
@Column(name = "INSTRUCTION_STATUS", nullable = false)
public InstructionStatus status;
@Column(name = "LAST_UPDATED", nullable = false)
public LocalDateTime lastUpdated;
@Column(name = "LAST_UPDATED_BY", nullable = false)
public String lastUpdatedBy;
//Getters & Setters
}
我研究过使用规范来执行此操作,并且我设法将查询分解为不同的规范,如下所示:
private Specification<Instruction> hasAccount(String account) {
return (root, query, criteriaBuilder) -> criteriaBuilder.in(root.get("account")).value(account);
}
private Specification<Instruction> havingStatus(List<String> status) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
final Subquery<Audit> auditSubquery = query.subquery(Audit.class);
final Root<Audit> audit = auditSubquery.from(Audit.class);
//select instruction id from audit where status is not in {status}
auditSubquery.select(audit.get("instruction").get("id"));
auditSubquery.where(criteriaBuilder.trim(audit.get("status")).in(status).not());
//Select instruction from table where
predicates.add(root.get("id").in(auditSubquery).not());
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
};
}
// Other specifications....
像这样调用时它们工作正常:
final List<Instruction> instructions = this.instructionRepository.findAll(
where(havingStatus(statuses)
.and(hasAccount(account))));
但我的目标是拥有它,例如我可以检查 if account == null
然后不包括 hasAccount
规范,对于其他可能为空的字段依此类推。我有办法做到这一点吗?
这应该可以解决问题。
Specification spec = where(null);
if (statuses != null) {
spec = spec.and(havingStatus(statuses))
}
if (account != null) {
spec = spec.and(hasAccount(account))
}
final List<Instruction> instructions = this.instructionRepository.findAll(spec);