I\'m writing java application using hibernate 5.2
but without HQL
there is two table, Transactions
and ResponseCode
You need to change your mapping. rcCode
cannot be an identifier because it does not identify records uniquely. I think that it will bring a lot of problems. ResponseCode
must have a different identifier.
@OneToOne
means one to one. You have one transaction, one response code but many languages.
You can mapping (@OneToOne
) a connection between a Transaction
and a ResponseCode
with a specific language (through a composite key).
You can use @OneToMany
, but usually in that case the reference should be from the ResponseCode
table to the Transaction
table.
But maybe you need 3 tables: transactions, response codes (with the code itself and its general information), response codes localizations (with messages on different languages). transactions one-to-one response_codes, response_codes one-to-many rc_localizations.
Or maybe you do not need hibernate-relation between Transaction
and ResponseCode
.
public class Transaction implements java.io.Serializable {
...
@Column(name="rc")
private String rc;
...
}
You can select the necessary ResponseCode
by code and language. Two selects: 1 - select Transaction
(with String rc-code); 2 - select ResponseCode
(with the necessary language) by rc-code from Transaction
.
You have mapped a single ResponseCode
entity in Transaction
, which is wrong. The response code is not a PK, it does not uniquely identifies a ResponseCode entity for a given Transaction entity. E.g. for a transaction with response code 000
there are 2 ResponseCode entities (with 'en' and 'ge' langs).
I recommend you to try to map a collection instead.
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="rc")
private List<ResponseCode> rcList;
Since your query WHERE conditions apply to the Transaction only, you can simply query the Transaction entities. Hibernate cache will optimize the eventual subqueries you need for each response code (one query for '000', one for '116', etc).
Finally I found out that
Criteria API does not support joining unrelated entities. JPQL does not support that either. However, Hibernate supports it in HQL since 5.1. https://discourse.hibernate.org/t/join-two-table-when-both-has-composite-primary-key/1966
Maybe there is some workarounds, but in this case, I think the better way is to use HQL instead of Criteria API.
Here is HQL implementation code snippet (nothing was changed in entity classes)
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();
Change the relation from @OneToOne
to @OneToMany
and use fetch
instead of join
, it will execute only one query and hopefully it works.
Join<Transaction, ResponseCode> join =
(Join<Transaction,ResponseCode>)transaction.fetch("rc",JoinType.LEFT);
and you can try it with @OneToOne
too.
Your mapping of both entities is wrong.
Let's start with the ResponseCode
entity. Your table model shows a composite primary key that consists of the RcCode
and Lang
columns. But your entity mapping only declares the rcCode
attribute as the primary key. You need to add an additional @Id
annotation to the rcLang
attribute of the ResponseCode
entity.
This should be the fixed mapping of the 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;
@Id
@Column(name = "rc_lang")
private String rcLang;
// Contructors and getters/setters
}
After fixing the primary key of your ReponseCode
entity, you need to reference both attributes/columns in the association mapping of your Transaction
entity. With Hibernate 5.2, you can do that with 2 of Hibernate's @JoinColumn
annotations. Older Hibernate versions and the JPA standard in version 2.1 need to wrap these annotations in an additional @JoinColumns
annotation.
Here is the fixed mapping of your Transaction
entity:
@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