当两个都具有复合主键时,休眠联接两个不相关的表

时间:2018-12-24 18:02:39

标签: java sql oracle hibernate jpa

我正在使用hibernate 5.2编写Java应用程序,但没有HQL

有两个表TransactionsResponseCode

enter image description here

我想由Hibernate生成的select语句的逻辑应该像下面的select波纹管

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生成两个select语句,第一个语句获取事务列表,第二个语句获取事务代码列表中包含的响应代码详细信息。

  

示例: 如果有30000个事务,并且15000个事务的响应代码为000,5000个事务的响应代码为116,而10000个事务的响应代码为400,则将运行第二条选择语句   三次,分别是000116和400 rcCode。

但是问题是ResponseCode表包含一种响应代码的几种语言 enter image description here

第一个选择语句包含对语言的限制,但是第二个选择语句不具有此限制,并且它不计量第一个语句中提供的语言,事务对象的最终结果包含某些事务en语言rc描述和某些事务ge语言rc描述。

  

我认为这取决于oracle最终选择了哪种语言描述

休眠生成的选择我

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

休眠生成的选择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 = ?'
  

目标 如果我建立OneToMany关系,它将获得30000笔交易,   执行30000个附加查询以获取每个的响应代码描述   操作

您知道如何解决吗?

5 个答案:

答案 0 :(得分:2)

将关系从@OneToOne更改为@OneToMany,并使用fetch代替join,它将仅执行一个查询,并希望它可以工作。

 Join<Transaction, ResponseCode> join =
        (Join<Transaction,ResponseCode>)transaction.fetch("rc",JoinType.LEFT);

,您也可以尝试使用@OneToOne

答案 1 :(得分:2)

最后我发现

  

标准API不支持加入不相关的实体。 JPQL确实   也不支持。但是,由于Hibernate在HQL中支持它,因为   5.1。 https://discourse.hibernate.org/t/join-two-table-when-both-has-composite-primary-key/1966

也许有一些解决方法,但是在这种情况下,我认为更好的方法是使用HQL而不是Criteria API。

这是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();

答案 2 :(得分:0)

您需要更改映射。 rcCode不能是标识符,因为它不能唯一地标识记录。我认为这会带来很多问题。 ResponseCode必须使用其他标识符。

@OneToOne表示一对一。您只有一笔交易,一份响应代码,但是有多种语言。

您可以使用特定的语言(通过composite key映射(@OneToOneTransactionResponseCode 之间的连接

您可以使用@OneToMany,但是通常在这种情况下,引用应该从ResponseCode表到Transaction表。

但是也许您需要3个表:事务,响应代码(带有代码本身及其一般信息),响应代码本地化(带有不同语言的消息)。一对一事务处理response_codes,response_codes一对多rc_localizations。

或者也许您不需要TransactionResponseCode之间的休眠关系。

public class Transaction implements java.io.Serializable {
...
    @Column(name="rc")
    private String rc;
...
}

您可以通过代码和语言选择必要的ResponseCode。两种选择:1-选择Transaction(使用String rc-code); 2-通过ResponseCode中的rc代码选择Transaction(具有必要的语言)。

答案 3 :(得分:0)

您对两个实体的映射是错误的。

让我们从ResponseCode实体开始。您的表模型显示了一个复合主键,该主键由RcCodeLang列组成。但是您的实体映射仅将rcCode属性声明为主键。您需要向@Id实体的rcLang属性添加附加的ResponseCode注释。

这应该是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的@JoinColumn注释中的2个来做到这一点。较早的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

答案 4 :(得分:0)

您已在ResponseCode中映射了一个Transaction实体,这是错误的。响应代码不是不是 PK,它不能唯一标识给定交易实体的ResponseCode实体。例如。对于具有响应代码000的交易,有2个ResponseCode实体(带有'en'和'ge'langs)。

我建议您尝试映射一个集合。

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="rc")
private List<ResponseCode> rcList;

由于查询“ WHERE”条件仅适用于“交易”,因此您只需查询“交易”实体即可。 Hibernate缓存将优化每个响应代码所需的最终子查询(一个查询“ 000”,一个查询“ 116”,等等)。