複数のパラメーターを持つJPA Criteria APIを使用する検索メソッドを作成する必要があります。問題は、すべてのパラメーターが必要なわけではないことです。そのため、一部はnullになる可能性があり、クエリに含めるべきではありません。私はCriteriaBuilderでこれを試しましたが、それを機能させる方法がわかりませんでした。
Hibernate Criteria APIを使用すると、これは非常に簡単です。基準を作成してから、制限を追加するだけです。
Criteria criteria = session.createCriteria(someClass.class);
if(someClass.getName() != null) {
criteria.add(Restrictions.like("name", someClass.getName());
}
JPAのCriteria APIで同じことをどのように達成できますか?
概念は、使用したい述語のみを含む javax.persistence.Predicate の配列を構築することです。
クエリされるエンティティの例:
@Entity
public class A {
@Id private Long id;
String someAttribute;
String someOtherAttribute;
...
}
クエリ自体:
//some parameters to your method
String param1 = "1";
String paramNull = null;
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery cq = qb.createQuery();
Root<A> customer = cq.from(A.class);
//Constructing list of parameters
List<Predicate> predicates = new ArrayList<Predicate>();
//Adding predicates in case of parameter not being null
if (param1 != null) {
predicates.add(
qb.equal(customer.get("someAttribute"), param1));
}
if (paramNull != null) {
predicates.add(
qb.equal(customer.get("someOtherAttribute"), paramNull));
}
//query itself
cq.select(customer)
.where(predicates.toArray(new Predicate[]{}));
//execute query and do something with result
em.createQuery(cq).getResultList();
このサイトをご覧ください JPA Criteria API 。たくさんの例があります。
更新:具体例を提供
特定の値よりも残高が少ないアカウントを検索してみましょう。
SELECT a FROM Account a WHERE a.balance < :value
最初に条件ビルダーを作成します
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Account> accountQuery = builder.createQuery(Account.class);
Root<Account> accountRoot = accountQuery.from(Account.class);
ParameterExpression<Double> value = builder.parameter(Double.class);
accountQuery.select(accountRoot).where(builder.lt(accountRoot.get("balance"), value));
結果を取得するには、パラメータを設定してクエリを実行します。
TypedQuery<Account> query = entityManager.createQuery(accountQuery);
query.setParameter(value, 1234.5);
List<Account> results = query.getResultList();
BTW:entityManagerはEJB/Service/DAOのどこかに挿入されます。
ミッコの答えは見事に機能しました。私がする必要のある変更は、交換することでした:
cq.select(customer).where(predicates.toArray(new Predicate[]{}));
で:
Predicate [] predicatesarr = predicates.toArray(new Predicate[predicates.size()]);
cq.select(customer).where(predicatesarr);
元のリストから配列への変換が機能しなかった場所。
まず、ミッコの答えは私の答えに私を導いた。そのために賛成票を投じる。
私のシナリオは、親子関係を望み、〜any〜の子に一致するものを見つけたいというものでした。
従業員には複数のJobTitleがあります。
私は従業員(多くの役職を持つ従業員)を見つけたいと思っていましたが、私が送ったすべての役職で見つけました。
SQLは次のようになります。
Dbo.Employeeから*を選択し、e.EmployeeKey = jt.EmployeeKeyのdbo.JobTitle jtに参加しますWHERE(jt.JobTitleName = 'programmer' OR jt.JobTitleName = 'badcop')
私は性別と生年月日を入れて例を完成させました(さらに「オプション」の基準を与えました)
私のJPAコード
import org.Apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import Java.util.ArrayList;
import Java.util.List;
public class MyEmployeeSpecification implements Specification<MyEmployee> {
private MyEmployee filter;
public MyEmployeeSpecification(MyEmployee filter) {
super();
this.filter = filter;
}
public Predicate toPredicate(Root<MyEmployee> root, CriteriaQuery<?> cq,
CriteriaBuilder cb) {
Predicate returnPred = cb.disjunction();
List<Predicate> patientLevelPredicates = new ArrayList<Predicate>();
if (filter.getBirthDate() != null) {
patientLevelPredicates.add(
cb.equal(root.get("birthDate"), filter.getBirthDate()));
}
if (filter.getBirthDate() != null) {
patientLevelPredicates.add(
cb.equal(root.get("gender"), filter.getGender()));
}
if (null != filter.getJobTitles() && filter.getJobTitles().size() > 0) {
List<Predicate> jobTitleLevelPredicates = new ArrayList<Predicate>();
Join<JobTitle, JobTitle> hnJoin = root.join("jobtitles");
for (JobTitle hnw : filter.getJobTitles()) {
if (null != hnw) {
if (StringUtils.isNotBlank(hnw.getJobTitleName())) {
jobTitleLevelPredicates.add(cb.equal(hnJoin.get("getJobTitleName"), hnw.getFamily()));
}
}
}
patientLevelPredicates.add(cb.or(jobTitleLevelPredicates.toArray(new Predicate[]{})));
}
returnPred = cb.and(patientLevelPredicates.toArray(new Predicate[]{}));
return returnPred;
}
}
しかし、私はpredicates.toArray(new Predicate [] {})、別名、可変引数のトリックのために私が考え出した。 (ミッコありがとう)
また、「implements Specifiction」メソッドも実行しています。
その他の役立つリンク:
ラムダ式を使用したSpringの簡単なソリューション:
Specification<User> specification = (root, query, builder) -> {
List<Predicate> predicates = new ArrayList<>();
if (criteria.getName() != null) {
// like
predicates.add(builder.like(root.get("name"), "%" + criteria.getName() + "%"));
}
if (criteria.getParentId() != 0) {
// equal
predicates.add(builder.equal(root.get("parent"), criteria.getParentId()));
}
// AND all predicates
return builder.and(predicates.toArray(new Predicate[0]));
};
repository.findAll(specification);