CriteriaBuilderを使用して、2つのテーブルを結合するクエリを作成します。 MySQLでは、私が作成しようとしているクエリは次のようになります。
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
AND item.type_id = 1
すべての注文を取得したいのですが、タイプ1のアイテムがある場合は、このアイテムに参加したいと思います。ただし、タイプ#1のアイテムが見つからない場合でも、注文を取得したいと思います。 CriteriaBuilderでこれを行う方法がわかりません。私が作る方法を知っているのは、
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
Join<Item, Type> type = order.join(Item_.type, JoinType.LEFT);
cq.select(order);
cq.where(cb.equal(type.get(Type_.id), 1));
このクエリは、MySQLで次のような結果になるため、壊れています。
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
WHERE item.type_id = 1
結果には、タイプ#1のアイテムの注文のみが含まれます。なしの注文は除外されます。最初の例のように、CriteriaBuilderを使用してクエリを作成するにはどうすればよいですか?
on
メソッドJoin<Z, X> on(Predicate... restrictions);
を使用することができます
方法は次のとおりです。
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
item.on(cb.equal(item.get(Item_.type), 1));
これはこの質問と同じ問題だと思います。 CriteriaBuilderでは不可能のようです。これはHibernate Criteria APIで可能ですが、おそらく役に立たないでしょう。
ON句はHibernate 4.3バージョンでサポートされていますが、追加のカスタム条件のパラメーターインデックスと既存のマッピングフィルターのインデックスの間にON句で外部結合を行うときにパラメーターインデックスの問題があるかどうかは誰でも知っていますか?
例として以下のPersonエンティティクラスを使用して、このフィルターを追加してアドレスの種類を制限し、フィルターがIN句に入力できるようにします。 ON句の一部の条件(「ストリート」列の使用など)を追加すると、IN句のパラメーターインデックスによって問題[2]が発生します。既知の問題ですか?
[1] @Filter(name = "addressTypes"、condition = "ADDRESS_TYPE in(:supportedTypes)")
[2]原因:エラー22018:タイプBIGINTの文字列フォーマットが無効です。プライベートセットアドレス。
JPA 2.0でHibernate 3.6を使用している場合、回避策があります。これは優れたソリューションではありませんが、私にとっては完璧に機能します。
@Where hibernateアノテーションを使用してエンティティを複製しました。つまり、このエンティティとの結合を使用するたびに、Hibernateは生成されたSQLの結合ステートメントに追加の条件を追加します。
たとえば、最初は次の例があります。
@Entity
@Table(name = "PERSON")
public class Person {
@Id
@Column(name = "PERSON_ID")
private Long id;
@Id
@Column(name = "PERSON_NAME")
private String name;
@OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
}
@Entity
@Table(name = "ADDRESS")
public class Address {
@Id
@Column(name = "ADDRESS_ID")
private Long id;
@Id
@Column(name = "ADDRESS_STREET")
private String street;
@ManyToOne
@JoinColumn(name = "PERSON_ID")
private Person person;
}
結合条件に追加の条件を追加するには、アドレス@エンティティマッピングを複製し、@ Whereアノテーション@Where(clause = "ADDRESS_TYPE_ID = 2")を追加する必要があります。
@Entity
@Table(name = "ADDRESS")
@Where(clause = " ADDRESS_TYPE_ID = 2")
public class ShippingAddress {
@Id
@Column(name = "ADDRESS_ID")
private Long id;
@Id
@Column(name = "ADDRESS_STREET")
private String street;
@OneToOne
@JoinColumn(name = "PERSON_ID")
private Person person;
}
また、新しいエンティティに重複するマッピングの関連付けを追加する必要があります。
@Entity
@Table(name = "PERSON")
public class Person {
@Id
@Column(name = "PERSON_ID")
private Long id;
@Id
@Column(name = "PERSON_NAME")
private String name;
@OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
@OneToOne(mappedBy = "person")
private ShippingAddress shippingAddress;
}
最後に、条件でこの特定のエンティティとの結合を使用できます。
PersonRoot.join(Person_.shippingAddress, JoinType.LEFT);
Hibernate Snippet SQLは次のようになります。
left outer join
address shippingadd13_
on person11_.person_id=shippingadd13_.person_id
and (
shippingadd13_.ADDRESS_TYPE_ID = 2
)
私はこの質問が長い間行われたことを知っていますが、最近同じ問題があり、Oracleフォーラムからこの解決策を見つけました。リンクが利用できなくなった場合に備えて、コピーして貼り付けました。
MiguelChillitupaArmijos 2011-abr-2011 1:41(en respuesta a 840578)次のようなものを使用する必要があると考えます:
em.createQuery("SELECT DISTINCT e.Id" +
" from Email e " +
" left join e.idEmailIn e2 *with* e2.responseType = 'response'" +
" where e.type = 'in' and e.responseMandatory = true").getSingleResult();
これがリンクです。