The ER diagram of the test db is :
I reverse engineered the Hibernate entities(and made some changes to the cascade) which are as follows :
Account.java
Code:
public class Account implements java.io.Serializable {
private Integer id;
private String reference;
private BigDecimal balance;
private String currency;
private boolean valid;
private String type;
private Set transactionlegs = new HashSet(0);
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((reference == null) ? 0 : reference.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Account other = (Account) obj;
if (reference == null) {
if (other.reference != null)
return false;
} else if (!reference.equals(other.reference))
return false;
return true;
}
.
.
}
Account.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated May 18, 2016 3:49:50 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
<class name="com.bank.entity.Account" table="account"
catalog="banktest" optimistic-lock="version">
<id name="id" type="java.lang.Integer">
<column name="Id" />
<generator class="identity" />
</id>
<property name="reference" type="string">
<column name="Reference" length="20" not-null="true" unique="true" />
</property>
<property name="balance" type="big_decimal">
<column name="Balance" not-null="true" />
</property>
<property name="currency" type="string">
<column name="Currency" length="3" not-null="true" />
</property>
<property name="valid" type="boolean">
<column name="Valid" not-null="true" />
</property>
<property name="type" type="string">
<column name="Type" length="20" />
</property>
<set name="transactionlegs" table="transactionleg" inverse="true"
lazy="true" fetch="select">
<key>
<column name="AccountId" not-null="true" />
</key>
<one-to-many class="com.bank.entity.Transactionleg" />
</set>
</class>
</hibernate-mapping>
Transaction.java
Code:
public class Transaction implements java.io.Serializable {
private Integer id;
private String type;
private String reference;
private Date createdDate;
private Date updatedDate;
private Set transactionlegs = new HashSet(0);
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((createdDate == null) ? 0 : createdDate.hashCode());
result = prime * result + ((reference == null) ? 0 : reference.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Transaction other = (Transaction) obj;
if (createdDate == null) {
if (other.createdDate != null)
return false;
} else if (!createdDate.equals(other.createdDate))
return false;
if (reference == null) {
if (other.reference != null)
return false;
} else if (!reference.equals(other.reference))
return false;
return true;
}
.
.
}
Transaction.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated May 17, 2016 1:56:15 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
<class name="com.bank.entity.Transaction" table="transaction"
catalog="banktest" optimistic-lock="version">
<id name="id" type="java.lang.Integer">
<column name="Id" />
<generator class="identity" />
</id>
<property name="type" type="string">
<column name="Type" length="20" not-null="true">
<comment>e.g : transfer of funds from one account to other, cash
withdrawal</comment>
</column>
</property>
<property name="reference" type="string">
<column name="Reference" length="20" not-null="true" />
</property>
<property name="createdDate" type="timestamp">
<column name="CreatedDate" length="19" not-null="true" />
</property>
<property name="updatedDate" type="timestamp">
<column name="UpdatedDate" length="19" not-null="true" />
</property>
<set name="transactionlegs" table="transactionleg" inverse="true"
lazy="true" fetch="select" cascade="save-update,delete">
<key>
<column name="TransactionId" not-null="true" />
</key>
<one-to-many class="com.bank.entity.Transactionleg" />
</set>
</class>
</hibernate-mapping>
Transactionleg.java
Code:
public class Transactionleg implements java.io.Serializable {
private Integer id;
private Account account;
private Transaction transaction;
private BigDecimal amount;
private String type;
private String currency;
private byte leg;
private boolean isValid;
private Date createdDate;
private Date updatedDate;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((account == null) ? 0 : account.hashCode());
result = prime * result + ((amount == null) ? 0 : amount.hashCode());
result = prime * result + ((createdDate == null) ? 0 : createdDate.hashCode());
result = prime * result + ((currency == null) ? 0 : currency.hashCode());
result = prime * result + leg;
result = prime * result + ((transaction == null) ? 0 : transaction.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Transactionleg other = (Transactionleg) obj;
if (account == null) {
if (other.account != null)
return false;
} else if (!account.equals(other.account))
return false;
if (amount == null) {
if (other.amount != null)
return false;
} else if (!amount.equals(other.amount))
return false;
if (createdDate == null) {
if (other.createdDate != null)
return false;
} else if (!createdDate.equals(other.createdDate))
return false;
if (currency == null) {
if (other.currency != null)
return false;
} else if (!currency.equals(other.currency))
return false;
if (leg != other.leg)
return false;
if (transaction == null) {
if (other.transaction != null)
return false;
} else if (!transaction.equals(other.transaction))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
Transactionleg.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated May 17, 2016 1:56:15 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
<class name="com.bank.entity.Transactionleg" table="transactionleg" catalog="banktest" optimistic-lock="version">
<id name="id" type="java.lang.Integer">
<column name="Id" />
<generator class="identity" />
</id>
<many-to-one name="account" class="com.bank.entity.Account" fetch="select">
<column name="AccountId" not-null="true" />
</many-to-one>
<many-to-one name="transaction" class="com.bank.entity.Transaction" fetch="select" cascade="save-update,delete">
<column name="TransactionId" not-null="true" />
</many-to-one>
<property name="amount" type="big_decimal">
<column name="Amount" not-null="true" />
</property>
<property name="type" type="string">
<column name="Type" length="10" not-null="true" />
</property>
<property name="currency" type="string">
<column name="Currency" length="3" not-null="true" />
</property>
<property name="leg" type="byte">
<column name="Leg" not-null="true" />
</property>
<property name="isValid" type="boolean">
<column name="IsValid" not-null="true" />
</property>
<property name="createdDate" type="timestamp">
<column name="CreatedDate" length="19" not-null="true" />
</property>
<property name="updatedDate" type="timestamp">
<column name="UpdatedDate" length="19" not-null="true" />
</property>
</class>
</hibernate-mapping>
Now, suppose there is a
Transaction with 4
Transactionleg and each leg does some change to an
Account. I wish to execute a single query, probably persist only the
Transaction which auto-inserts the
Transactionleg and also updates the
Account :
Code:
Set<Transactionleg> transactionLegDbRecords = new HashSet<>();
for (TransactionLeg transactionLeg : transactionLegs) {
Transactionleg transactionleg = new Transactionleg(
transactionDbRecord, transactionLeg.getAmount().getAmount(),
(transactionLeg.getAmount().getAmount().signum() == -1 ? "DEBIT" : "CREDIT"),
transactionLeg.getAmount().getCurrency().getCurrencyCode(),
(byte) transactionLegs.indexOf(transactionLeg), true, timeStamp, timeStamp);
transactionLegDbRecords.add(transactionleg);
log.debug("Transactionleg is " + transactionleg);
}
transactionDbRecord.setTransactionlegs(transactionLegDbRecords);
TransactionDao transactionDao = new TransactionDaoImpl();
transactionDao.save(transactionDbRecord);
I'm unsure if my expectation is fair because :
Logically/sensible at a database query level, I am uncomfortable with the main Account record being left at the mercy of the child updates
How to do this in Hibernate for all the scenarios e.g: persist Account and let Transaction and Transactionleg be updated/created auto.
Of course, in all the cases,
data consistency is must - one failure should cause rollback and keep all the entities intact.What am I missing ?