wake-up-neo.com

Ruhezustand Verbinden Sie zwei nicht miteinander verknüpfte Tabellen, wenn beide über den zusammengesetzten Primärschlüssel verfügen

Ich schreibe Java-Anwendungen mit hibernate 5.2 aber ohne HQL

es gibt zwei Tabellen, Transactions und ResponseCode

 enter image description here

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  enter image description here

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?

12
Jibo

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();
2
Jibo

Ä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.

2
Khalid Shah

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 ResponseCodemit 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.

0

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
0
Thorben Janssen

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.).

0
gaborsch