Ich schreibe Java-Anwendungen mit hibernate 5.2
aber ohne HQL
es gibt zwei Tabellen, Transactions
und ResponseCode
Die Logik der select-Anweisung, die ich von Hibernate generieren möchte, sollte folgendermaßen aussehen
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 )
Aber in meinem Code stimmt etwas nicht, hier ist mein Implementierungs-Snippet
Transaktionsobjekt
@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
@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
Implementierungscode
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);
Ruhezustand Erzeugt zwei select-Anweisungen, wobei die erste Anweisung die Transaktionsliste und die zweite Anweisung die Antwortcode-Details abruft, die in der Transaktionsliste enthalten sind.
example: _ Wenn es eine 30000-Transaktion gibt und 15000 Transaktion einen Antwortcode 000 hat, die Transaktion 5000 einen Antwortcode 116 hat und 10000 einen Antwortcode 400 hat, führt sie die zweite select-Anweisung dreimal aus, z 000,116 und 400 RC-Code.
das Problem ist jedoch, dass die ResponseCode
-Tabelle mehrere Sprachen für einen Antwortcode enthält
die erste Auswahlanweisung enthält die Einschränkung für die Sprache, aber für die zweite Auswahlanweisung gilt diese Einschränkung nicht, und sie misst nicht, welche Sprache in der ersten Anweisung angegeben wird. Das Endergebnis des Transaktionsobjekts enthält für einige Transaktionen en
die Sprachbeschreibung und für einige Transaktionen ge
Beschreibungen der Sprache.
Ich denke, es hängt davon ab, welche Sprachbeschreibung zuletzt von Oracle ausgewählt wurde.
Ruhezustand generiert auswählen 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
Ruhezustand generiert auswählen 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 = ?'
P.s _ Wenn ich die Variable
OneToMany
mache, erhält sie eine 30000-Transaktion und Führt eine zusätzliche 30000-Abfrage aus, um eine Antwort zu erhalten. Code-Beschreibung für jede -Operation
Wissen Sie, wie Sie das Problem beheben können?
Schließlich habe ich das herausgefunden
Die Kriterien-API unterstützt den Beitritt nicht verbundener Entitäten nicht. JPQL tut das unterstützt das auch nicht. Hibernate unterstützt es jedoch seit .__ in HQL. 5.1. https://discourse.hibernate.org/t/join-two-table-when-both-has-composite-primary-key/1966
Möglicherweise gibt es einige Problemumgehungen, aber in diesem Fall denke ich, dass der bessere Weg ist, HQL anstelle von Criteria API zu verwenden.
Hier ist ein Code-Snippet für die HQL-Implementierung (in Entitätsklassen wurde nichts geändert)
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();
Ändern Sie die Beziehung von @OneToOne
in @OneToMany
und verwenden Sie fetch
anstelle von join
. Es wird nur eine Abfrage ausgeführt, und hoffentlich funktioniert es.
Join<Transaction, ResponseCode> join =
(Join<Transaction,ResponseCode>)transaction.fetch("rc",JoinType.LEFT);
und Sie können es auch mit @OneToOne
versuchen.
Sie müssen Ihre Zuordnung ändern. rcCode
kann kein Bezeichner sein, da Datensätze nicht eindeutig identifiziert werden. Ich denke, das bringt viele Probleme mit sich. ResponseCode
muss einen anderen Bezeichner haben.
@OneToOne
bedeutet eins zu eins. Sie haben eine Transaktion, einen Antwortcode, aber viele Sprachen.
Sie können eine Verbindung zwischen einer Transaction
und einer ResponseCode
mit einer bestimmten Sprache zuordnen (@OneToOne
) (über einen composite key ).
Sie können @OneToMany
verwenden, aber in diesem Fall sollte der Verweis von der ResponseCode
-Tabelle auf die Transaction
-Tabelle verweisen.
Möglicherweise benötigen Sie jedoch 3 Tabellen: Transaktionen, Antwortcodes (mit dem Code selbst und seinen allgemeinen Informationen), Antwortcode-Lokalisierungen (mit Nachrichten in verschiedenen Sprachen). Transaktionen Eins-zu-Eins-Antwortkodes, Antwortkodes Eins-zu-Viele-RC-Lokalisierungen.
Oder Sie benötigen möglicherweise keine Hibernate-Beziehung zwischen Transaction
und ResponseCode
.
public class Transaction implements Java.io.Serializable {
...
@Column(name="rc")
private String rc;
...
}
Sie können die erforderliche ResponseCode
nach Code und Sprache auswählen. Zwei Auswahlmöglichkeiten: 1 - Wählen Sie Transaction
(mit String RC-Code); 2 - Wählen Sie ResponseCode
(mit der erforderlichen Sprache) per RC-Code aus Transaction
.
Ihre Zuordnung beider Entitäten ist falsch.
Beginnen wir mit der Entität ResponseCode
. Ihr Tabellenmodell zeigt einen zusammengesetzten Primärschlüssel, der aus den Spalten RcCode
und Lang
besteht. Ihre Entitätszuordnung deklariert jedoch nur das Attribut rcCode
als Primärschlüssel. Sie müssen dem rcLang
-Attribut der ResponseCode
-Entität eine zusätzliche @Id
-Anmerkung hinzufügen.
Dies sollte die feste Zuordnung der ResponseCode
-Entität sein:
@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
}
Nach dem Festlegen des Primärschlüssels Ihrer ReponseCode
-Entität müssen Sie beide Attribute/Spalten in der Zuordnungszuordnung Ihrer Transaction
-Entität referenzieren. Mit Hibernate 5.2 können Sie dies mit zwei @JoinColumn
-Anmerkungen von Hibernate machen. Ältere Hibernate-Versionen und der JPA-Standard in Version 2.1 müssen diese Annotationen in eine zusätzliche @JoinColumns
-Annotation einschließen.
Hier ist die feste Zuordnung Ihrer Transaction
-Entität:
@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
Sie haben eine einzelne ResponseCode
-Entität in Transaction
zugeordnet, die falsch ist. Der Antwortcode ist nicht ein PK. Er identifiziert eine ResponseCode-Entität für eine bestimmte Transaktionsentität nicht eindeutig. Z.B. Für eine Transaktion mit dem Antwortcode 000
gibt es 2 ResponseCode-Entitäten (mit 'en' und 'ge' langs).
Ich empfehle Ihnen stattdessen, eine Sammlung abzubilden.
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="rc")
private List<ResponseCode> rcList;
Da Ihre Abfrage WHERE-Bedingungen nur für die Transaktion gelten, können Sie die Transaktionsentitäten einfach abfragen. Im Hibernate-Cache werden eventuell erforderliche Unterabfragen für jeden Antwortcode optimiert (eine Abfrage nach '000', eine nach '116' usw.).