Hello All,
I'm working on a simple budget web-app and have run into a problem with NonUniqueObjectException. Here's the basic setup:
Step 1: I've got an Account (for example, a "Checking" account) with a pre-existing balance of $100.00. I add a transaction (for example, a "groceries" transaction) of $25.00. That action affects my Account balance and my account balance is now $75.00.
Step 2: A few days later I come back to my budgeting program and realize I've mistakenly entered $25.00 for my transaction when I meant to enter $50.00. So I click on edit transaction, put the new value of $50.00 in, and click save. Then the program blows up with a "NonUniqueObjectException" exception and my changes are unsaved due to the transaction rollback.
Now I have a servlet front end layer which is making calles to a services layer which is making calls to a DAO layer. The database transaction demarcation is done through Spring on the services layer methods. So a new database transaction is started when the front end calls a service method and the transaction is committed when the service method returns without exception.
The code for addTransaction() in my services layer executed for step #1 above works just fine. The pseudo-code for that method is thus:
Code:
addTransaction(Transaction transaction)
{
Account account = transaction.getAccount();
account.setBalance(account.getBalance() - transaction.getAmount());
AccountServices.updateAccount(account);
TransactionDAO.addTransaction(transaction);
}
TransactionDAO.addTransaction(Transaction transaction)
{
this.hibernateTemplate.save(transaction);
}
But things blow up and get complicated with updateTransaction() for step #2 above:
Code:
updateTransaction(Transaction transaction)
{
//get the currently saved version of the transaction out of the db
// so we can remove the old transaction amount from the
// associated account
Transaction oldTransaction = transactionDAO.getTransaction(transaction.getId());
Account account = oldTransaction.getAccount();
account.setBalance(account.getBalance() + oldTransaction.getAmount());
AccountServices.updateAccount(account);
//now that we've effectively removed the old transaction amount
// from the account's balance, we need to add the update transaction
// amount back into whichever account it's interested in
// Note that the account associated with the transaction could
// have changed so we can't rely on the account given from
// oldTransaction..
account = transaction.getAccount();
account.setBalance(account.getBalance() - transaction.getAmount());
AccountServices.updateAccount(account);
TransactionDAO.updateTransaction(transaction);
}
TransactionDAO.updateTransaction(Transaction transaction)
{
this.hibernateTemplate.update(transaction);
}
That of course throws a NonUniqueObjectException on the updateTransaction() line, if one's not thrown on the second updateAccount() call due to multiple versions of an account isntance floating around.
I've looked at the common problems FAQ answer for this problem (
http://www.hibernate.org/116.html#A8), but the suggestion there is that i should call saveorupdate before doing anything else. Obviously that's not possible in this case because that would save over the original transaction amount I'm trying to get at.
I also tried to do this:
Code:
TransactionDAO.updateTransaction(Transaction transaction)
{
Transaction mergedTransaction = this.hibernateTemplate.merge(transaction);
this.hibernateTemplate.update(mergedTransaction );
}
but that also had the same nonunique exception :/
I can't figure out a sane way to address this problem with a single "Transaction" object floating around. I really need the old version of the transaction to make my business logic straightforward in that method above, but this NonUnique exception is forcing me to rethink my logic in several ways that seem screwy to me. It seems I should be able to tell the hibernatesession to "forget" about the "oldTransaction" object above and still be able to have my database transaction rollback options open if something goes wrong. If i flush() on the hibernate session will that screw up my chances at a database transaction rollback? If i flush() will that get rid of the session's knowledge of the "oldTransaction" object? Is there something else I should do?
How would you guys suggest solving this problem? Is there something simple I'm missing in hibernate mapping or configuration or usage that'd remedy this problem? Perhaps there's even a way to make hibernate handle my data integrity in this simple budgetting account balance -> transaction amounts case?
[/code]