-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: Failing to read back data written in the same transaction
PostPosted: Wed Oct 11, 2006 4:29 pm 
Beginner
Beginner

Joined: Tue Sep 19, 2006 11:26 am
Posts: 33
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


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 12, 2006 4:11 pm 
Beginner
Beginner

Joined: Tue Sep 19, 2006 11:26 am
Posts: 33
I have figured out a workaround/fix for this problem. Although I didn't put it in the code above, at the start of the method, before I enter the while loop I set the flush mode type to commit:
Code:
entityManager.setFlushMode(FlushModeType.COMMIT);


I expected this to mean that nothing would be commited to the database until the transaction was commited (i.e. the method described completes). This is true but I thought that it would be fine for me to be able to access entries that I had written within the same transaction.

Commenting this code out (effectively setting the flush type mode to AUTO) solved my problem. However this had a major effect on performance so I needed to find a better work around.

I found that by adding the following:
Code:
entityManager.flush();
entityManager.clear();

to the end of the persist and merge methods meant that I could read back the data I had written and then uncommenting the setFlushMode call above gave me back the performance without any of the other side effects.

Can anyone explain to me why as I have now spent two very long days figuring this out. Although I am happy its fixed my problem, I don't see why I should have to do it.

Thanks.

Simon


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.