我正在使用hibernate 5.2
编写Java应用程序,但没有HQL
有两个表Transactions
和ResponseCode
我想由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
表包含一种响应代码的几种语言
第一个选择语句包含对语言的限制,但是第二个选择语句不具有此限制,并且它不计量第一个语句中提供的语言,事务对象的最终结果包含某些事务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个附加查询以获取每个的响应代码描述 操作
您知道如何解决吗?
答案 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映射(@OneToOne
和Transaction
和ResponseCode
之间的连接。
您可以使用@OneToMany
,但是通常在这种情况下,引用应该从ResponseCode
表到Transaction
表。
但是也许您需要3个表:事务,响应代码(带有代码本身及其一般信息),响应代码本地化(带有不同语言的消息)。一对一事务处理response_codes,response_codes一对多rc_localizations。
或者也许您不需要Transaction
和ResponseCode
之间的休眠关系。
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
实体开始。您的表模型显示了一个复合主键,该主键由RcCode
和Lang
列组成。但是您的实体映射仅将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”,等等)。