Hi
I have the following entity beans which I am using with Embedded EJB to write data to a MySQL 4 database:
Code:
/**
* Account transactions
*/
@Entity
@Table(name = "account_tx", catalog = "ocss", uniqueConstraints = {})
public class AccountTxSkel implements java.io.Serializable
{
// Fields
/**
*
*/
private static final long serialVersionUID = 1L;
private int id;
/* There are other fields but I've removed them for clarity */
private Set<AccountTxIssueSkel> accountTxIssues = new HashSet<AccountTxIssueSkel>(0);
// Constructors
/** default constructor */
public AccountTxSkel()
{
}
/** full constructor */
public AccountTxSkel(int id, Set<AccountTxIssueSkel> accountTxIssues)
{
this.id = id;
this.accountTxIssues = accountTxIssues;
}
// Property accessors
@Id
@GeneratedValue
@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = true)
public int getId()
{
return this.id;
}
public void setId(int id)
{
this.id = id;
}
@OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, mappedBy = "accountTx")
public Set<AccountTxIssueSkel> getAccountTxIssues()
{
return this.accountTxIssues;
}
public void setAccountTxIssues(Set<AccountTxIssueSkel> accountTxIssues)
{
this.accountTxIssues = accountTxIssues;
}
}
/**
* Account transaction issue - a transaction can cover multiple issues.
*/
@Entity
@Table(name = "account_tx_issue", catalog = "ocss", uniqueConstraints = {})
public class AccountTxIssueSkel implements java.io.Serializable, Comparable<AccountTxIssueSkel>
{
private static final long serialVersionUID = 1L;
// Fields
private AccountTxIssueId id;
private AccountTxSkel accountTx;
// Constructors
/** default constructor */
public AccountTxIssueSkel()
{
}
/** full constructor */
public AccountTxIssueSkel(AccountTxIssueId id, AccountTxSkel accountTx)
{
this.id = id;
this.accountTx = accountTx;
}
// Property accessors
@EmbeddedId
@AttributeOverrides( {
@AttributeOverride(name = "accountTxId", column = @Column(name = "account_tx_id", unique = false, nullable = false, insertable = true, updatable = true)),
@AttributeOverride(name = "issue", column = @Column(name = "issue", unique = false, nullable = false, insertable = true, updatable = true)) })
public AccountTxIssueId getId()
{
return this.id;
}
public void setId(AccountTxIssueId id)
{
this.id = id;
}
@ManyToOne(cascade = {}, fetch = FetchType.LAZY)
@JoinColumn(name = "account_tx_id", unique = false, nullable = false, insertable = false, updatable = false)
public AccountTxSkel getAccountTx()
{
return this.accountTx;
}
public void setAccountTx(AccountTxSkel accountTx)
{
this.accountTx = accountTx;
}
public int compareTo(final AccountTxIssueSkel compare)
{
return this.id.getIssue() - compare.id.getIssue();
}
}
/**
* Account transaction issue composite primary key.
*/
@Embeddable
public class AccountTxIssueId implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
// Fields
private int accountTxId;
private int issue;
// Constructors
/** default constructor */
public AccountTxIssueId()
{
}
/** full constructor */
public AccountTxIssueId(int accountTxId, int issue)
{
this.accountTxId = accountTxId;
this.issue = issue;
}
// Property accessors
@Column(name = "account_tx_id", unique = false, nullable = false, insertable = true, updatable = true)
public int getAccountTxId()
{
return this.accountTxId;
}
public void setAccountTxId(int accountTxId)
{
this.accountTxId = accountTxId;
}
@Column(name = "issue", unique = false, nullable = false, insertable = true, updatable = true)
public int getIssue()
{
return this.issue;
}
public void setIssue(int issue)
{
this.issue = issue;
}
public boolean equals(Object other)
{
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof AccountTxIssueId))
return false;
AccountTxIssueId castOther = (AccountTxIssueId) other;
return (this.getAccountTxId() == castOther.getAccountTxId())
&& (this.getIssue() == castOther.getIssue());
}
public int hashCode()
{
int result = 17;
result = 37 * result + this.getAccountTxId();
result = 37 * result + this.getIssue();
return result;
}
}
What I am attempting to do is to create a number of account transaction entries and then create multiple account transaction issue entries for each of the account transactions, within a EJB transaction. With code along the lines of the following:
Code:
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public int doImport(final File file)
throws Exception
{
boolean newTrans = false;
while (//There are more transactions to create) {
final AccountTxSkel transaction;
final List<AccountTxSkel> transactions =
getAccountTxSkelDao().find(// Various parameters);
switch (transactions.size()) {
case 0:
newTrans = true;
transaction = new AccountTxSkel();
break;
case 1:
transaction = transactions.get(0);
break;
default:
throw new Exception("Should only be one transaction for given parameters");
}
// For some reason the account transactions issues are not getting automatically
// read so trying to do it explicitly
final Set<AccountTxIssueSkel> issues = new HashSet<AccountTxIssueSkel>();
if (!newTrans) {
issues.addAll(accountTxIssueSkelDao().findByAccountTxId(transaction.getId()));
}
// Various other processing using amongst other things the list of
// account transaction issues
// Does a persist or merge based on the id being 0 or not
accountTxSkelDao.saveOrUpdate(transaction);
final AccountTxIssueSkel issue = new AccountTxIssueSkel();
final AccountTxIssueId id = new AccountTxIssueId();
id.setIssue(currIssue);
id.setAccountTxId(transaction.getId());
issue.setId(id);
if (newTrans) {
accountTxIssueSkelDao().persist(issue);
}
else {
// This maybe a insert or an update, it isn't possible to tell without
// doing a select ourselves.
accountTxIssueSkelDao().merge(issue);
}
}
}
The problem that I am having is that with the above code the first time through the loop a new account transaction is created and a new account transaction issue. The next time through the loop it reads back the first transaction and the set of account transaction issues contains the first account transaction issue written away it then creates a second account transaction issue. The third time through the loop it again reads in the original account transaction but the set of transaction issues still only contains the first account transaction issue - the second one doesn't get read in.
On subsequent passes through the loop, if a new transaction is created the same thing happens for the new transaction - I only manage to read back the first account transaction issue for it.
Once the code has completed and I examine what is in the database all of the account transactions and account transaction issue entries that I would expect to be in there are in there.
When I first wrote the code rather than explicitly reading in the account transaction issues with:
Code:
if (!newTrans) {
issues.addAll(accountTxIssueSkelDao().findByAccountTxId(transaction.getId()));
}
I was trying to use transaction.getAccountTxIssues() but this always returned an empty set. I tried setting the fetch type on this to be EAGER but that made no difference either.
The only way I have managed to get this code to work is to move the body of the while loop into a separate method and set the TransactionAttributeType to NOT_SUPPORTED which results in a new EJB transaction being started and then committed each time through the loop. The problem with this though is that I don't want any of the account transactions to be committed to the database until I have processed them all. One thing that might be worth noting is that I had to set the FetchType to EAGER for this to work see below:
Code:
@OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, mappedBy = "accountTx")
public Set<AccountTxIssueSkel> getAccountTxIssues()
{
return this.accountTxIssues;
}
Any help on this would be greatly appreciated - I have well and truly come up against a brick wall with it.
Simon