Java hibernate 5.2
ですが、HQL
はありません
Transactions
とResponseCode
の2つのテーブルがあります
Hibernateで生成するselectステートメントのロジックは、次のselect bellowのようになります。
SELECT t.tranType
,t.tranId
,t.requestDate
,t.rcCode
,t.tranAmount
,r.description
,r.status
FROM transactions t
LEFT OUTER JOIN responseCode r
ON t.rcCode = r.rcCode
AND (r.lang = 'en')
WHERE (t.merchant_id =5 )
しかし、コードに問題があります。ここに私の実装スニペットがあります
トランザクションエンティティ
@Entity
@Table(name = "transactions")
public class Transaction implements Java.io.Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "merchant_id", nullable = true)
private String merchantID;
@Column(name = "tran_amount", nullable = true)
private String tranAmount;
@Id
@Column(name = "tran_type", nullable = true)
private String tranType;
@Column(name = "auth_request_date", nullable = true)
@Temporal(TemporalType.TIMESTAMP)
private Date authRequestDate;
@Id
@Column(name = "tran_id", nullable = true)
private String tranID;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="rc")
private ResponseCode rc;
// Contructos and getters/setters
ResponseCodeエンティティ
@Entity
@Table(name = "response_codes")
public class ResponseCode implements Java.io.Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "response_code")
private String rcCode;
@Column(name = "rc_status")
private String rcStatus;
@Column(name = "rc_description")
private String rcDesc;
@Column(name = "rc_lang")
private String rcLang;
// Contructos and getters/setters
実装コード
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Transaction> criteria = builder.createQuery(Transaction.class);
Root<Transaction> transaction = criteria.from(Transaction.class);
Join<Transaction, ResponseCode> bJoin = transaction.join("rc",JoinType.LEFT);
bJoin.on(builder.equal(bJoin.get("rcLang"), tRequest.getLang()));
Predicate predicate = builder.and(transaction.get("merchantID").in(tRequest.getMerchantList()));
predicate = builder.and(predicate, builder.between(transaction.get("authRequestDate"), dateFrom, dateTo));
criteria.where(predicate);
Hibernate 2つのselectステートメントを生成します。最初のステートメントはトランザクションリストを取得し、2番目のステートメントはトランザクションリストに含まれる応答コードの詳細を取得します。
例:30000トランザクションがあり、15000トランザクションに000応答コードがある場合、5000トランザクションには116応答コードと10000トランザクションがあります400の応答コードがあり、000,116と400のrcCodeに対して2番目の選択ステートメントを3回実行します。
しかし、問題は、ResponseCode
テーブルに1つの応答コードに複数の言語が含まれていることです
最初の選択ステートメントには言語の制限が含まれていますが、2番目の選択ステートメントにはこの制限がありません。また、最初のステートメントで提供される言語は測定されません。トランザクションオブジェクトの最終結果には、一部のトランザクションが含まれますen
言語rc説明一部のトランザクションでは、ge
言語rcの説明。
それは、最後にOracleによって選択された言語の説明に依存すると思います
Hibernateが生成したselect I
SELECT t.tran_type
,t.tran_id
,t.auth_request_date
,t.merchant_id
,t.rc
,t.tran_amount
FROM transactions t
LEFT OUTER JOIN response_codes r
ON t.rc = r.response_code
AND (r.rc_lang = ?)
WHERE (t.merchant_id IN (?))
AND (t.AUTH_REQUEST_DATE BETWEEN ? AND ?)
ORDER BY t.AUTH_REQUEST_DATE ASC
Hibernateによって生成された選択II
SELECT r.response_code
,r.rc_description
,r.rc_lang
,r.rc_status
FROM response_codes r
WHERE r.response_code = ?
//this select statement should have 'AND r.rc_lang = ?'
Ps
OneToMany
リレーションを作成すると、30000トランザクションを取得し、30000追加クエリを実行して応答を取得しますコードの説明N + 1の問題として知られている各操作について
あなたはそれを修正する方法を知っていますか?
最後に私はそれを見つけました
Criteria APIは、無関係なエンティティの結合をサポートしていません。 JPQLもサポートしていません。ただし、5.1以降、HibernateはHQLでサポートしています。 https://discourse.hibernate.org/t/join-two-table-when-both-has-composite-primary-key/1966
いくつかの回避策があるかもしれませんが、この場合は、Criteria APIの代わりにHQLを使用する方が良いと思います。
HQL実装のコードスニペットは次のとおりです(エンティティクラスでは何も変更されていません)
String hql = "FROM Transaction t \r\n" +
" LEFT OUTER JOIN FETCH t.rc r \r\n" +
" WHERE (t.merchantID IN (:merchant_id))\r\n" +
" AND (t.authRequestDate BETWEEN :from AND :to)\r\n" +
" AND (r.rcLang = :rcLang or r.rcLang is null)\r\n";
Query query = session.createQuery(hql,Transaction.class);
query.setParameter("merchant_id", tRequest.getMerchantList());
query.setParameter("rcLang", tRequest.getLang());
query.setParameter("from", dateFrom);
query.setParameter("to", dateTo);
List<Transaction> dbTransaction = query.getResultList();
リレーションを@OneToOne
から@OneToMany
に変更し、fetch
の代わりにjoin
を使用すると、クエリが1つだけ実行され、うまくいくと思います。
Join<Transaction, ResponseCode> join =
(Join<Transaction,ResponseCode>)transaction.fetch("rc",JoinType.LEFT);
@OneToOne
でもお試しいただけます。
マッピングを変更する必要があります。 rcCode
はレコードを一意に識別しないため、識別子にすることはできません。多くの問題が発生すると思います。 ResponseCode
には別の識別子が必要です。
@OneToOne
は1対1を意味します。 1つのトランザクション、1つの応答コードがありますが、多くの言語があります。
Transaction
とResponseCode
の間の接続を特定の言語でマッピング(@OneToOne
)できます( 複合キー を使用)。
@OneToMany
を使用できますが、通常、その場合、参照はResponseCode
テーブルからTransaction
テーブルへの参照である必要があります。
しかし、おそらく3つのテーブルが必要です。トランザクション、応答コード(コード自体とその一般情報を含む)、応答コードのローカリゼーション(さまざまな言語のメッセージを含む)。トランザクションは1対1のresponse_codes、response_codesは1対多のrc_localizationsです。
または、Transaction
とResponseCode
の間にhibernate関係が必要ない場合もあります。
public class Transaction implements Java.io.Serializable {
...
@Column(name="rc")
private String rc;
...
}
必要なResponseCode
をコードと言語で選択できます。 2つのselect:1-select Transaction
(with string rc-code); 2-ResponseCode
からrc-codeによってTransaction
(必要な言語で)を選択します。
両方のエンティティのマッピングが間違っています。
ResponseCode
エンティティから始めましょう。テーブルモデルには、RcCode
列とLang
列で構成される複合主キーが表示されます。ただし、エンティティマッピングではrcCode
属性を主キーとして宣言するだけです。 rcLang
エンティティのResponseCode
属性に追加の@Id
アノテーションを追加する必要があります。
これは、ResponseCode
エンティティの固定マッピングである必要があります。
@Entity
@Table(name = "response_codes")
public class ResponseCode implements Java.io.Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "response_code")
private String rcCode;
@Column(name = "rc_status")
private String rcStatus;
@Column(name = "rc_description")
private String rcDesc;
@Id
@Column(name = "rc_lang")
private String rcLang;
// Contructors and getters/setters
}
ReponseCode
エンティティの主キーを修正した後、Transaction
エンティティの関連付けマッピングで両方の属性/列を参照する必要があります。 Hibernate 5.2では、Hibernateの2つの@JoinColumn
アノテーションを使用してそれを行うことができます。古いHibernateバージョンとバージョン2.1のJPA標準では、これらのアノテーションを追加の@JoinColumns
アノテーションでラップする必要があります。
Transaction
エンティティの固定マッピングは次のとおりです。
@Entity
@Table(name = "transactions")
public class Transaction implements Java.io.Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "merchant_id", nullable = true)
private String merchantID;
@Column(name = "tran_amount", nullable = true)
private String tranAmount;
@Id
@Column(name = "tran_type", nullable = true)
private String tranType;
@Column(name = "auth_request_date", nullable = true)
@Temporal(TemporalType.TIMESTAMP)
private Date authRequestDate;
@Id
@Column(name = "tran_id", nullable = true)
private String tranID;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="rc_id", referencedColumnName = "id")
@JoinColumn(name="rc_lang", referencedColumnName = "lang")
private ResponseCode rc;
// Contructos and getters/setters
ResponseCode
に単一のTransaction
エンティティをマッピングしましたが、これは誤りです。応答コードはnot PKであり、特定のトランザクションエンティティのResponseCodeエンティティを一意に識別しません。例えば。応答コード000
のトランザクションの場合、2つのResponseCodeエンティティがあります( 'en'および 'ge'言語を使用)。
代わりにコレクションをマップすることをお勧めします。
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="rc")
private List<ResponseCode> rcList;
クエリのWHERE条件はトランザクションにのみ適用されるため、トランザクションエンティティをクエリするだけで済みます。 Hibernateキャッシュは、応答コードごとに必要な最終的なサブクエリを最適化します( '000'に対して1つのクエリ、 '116'に対して1つのクエリなど)。