Spring transaction propagation_required issue - spring

In our java project we are using ORM with hibernate and spring.
I had problems in deleting persistent objects. For example this sample method gets entities by ids and then delete them:
#Transactional
public void remove(List<Long> ids) {
SearchTemplate template = new SearchTemplate();
template.addParameter("milestoneId",ids);
List <InvoiceQueue> items = this.findByCriteria(template);
...
this.delete(items);
}
Method executes Ok without any exception but doesn't actually delete the items from the DB.
Adding the following annotation to the method definition #Transactional(propagation = Propagation.REQUIRES_NEW) solves the problem.
Can anyone explain why it doesn't work with the default propagation type PROPAGATION_REQUIRED.
Thanks in advance.
Environment details :
hibernate.version 3.5.5-Final, spring.version 3.0.5.RELEASE

Really just repeating what #PeterBagyinszki said in his comment, but the reason quite probably is that the transaction within which your delete occurs gets rolled back due to some other part throwing an exception, and all the changes made during the transaction get canceled. With Propagation.REQUIRES_NEW, the delete is done within it's own separate nested transaction. The outcome of the nested transaction (committed or rolled back) won't affect the "outer" transaction and vice versa.
Check your logs to see what is causing the transaction to be rolled back, note that even something like a simple SELECT -query failing with something like NoResultException will cause the transaction to roll back, unless you explicitly state in the #Transactional-annotation it not to roll back on certain exceptions.

Related

Spring getCurrentSession() and save sevaral object

Currently, I use openSession(), begin and commit transaction in a method with classic dao and service layers and save/update/delete multiple object in it. If any error exist I am sure that any save/update in this method did not done.
Is it possible to save more than one object in a method called with #Transactional annotation via getCurrentSession() and it is safe if this guarantee that all object in this method saved/updated or any did not done? And if possible how can I use rollback in this method?
Yes you can, with proper transactional semantics.
#Transactional has a property named rollbackFor = TypeOfException.class. By default it rolls back RuntimeException, but you can specify your own or just Exception for any checked exception.
You should have a proper propagation aswell which again you can specify as a property on #Transactional. Note that if you set propagation = REQUIRES_NEW and an existing transaction calls this method, that will rollback for the nested transaction only. What you're trying to achieve, the most common one I think is to specify propagation = REQUIRED - this will not run on its own transaction but proceed on the existing one, which means that on case of failure the whole transaction will rollback.

Do Spring transactions propagate through new instantiations

I'm working on a bunch of legacy code written by people before me and I'm confused about a particular kind of setup and wonder if this has ever worked to begin with.
There is a managed bean in spring that has a transactional method.
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
public boolean updateDraftAndLivePublicationUsingFastDocumentsOfMySite(List<FastDocumentLite> fastDocumentLites, Long mySiteId) throws Exception { ... }
Now inside that method I find new instantiations calling update methods fe:
boolean firstFeed = new MySiteIdUpdate(publishing, siteDao, siteDomainService).update(siteId, fastDocumentLites.get(0).getMySiteId());
From my understanding on IOC this new class isn't managed by spring , it's just a variable in the bean. Now going further inside the update method you see another service gets called.
#Transactional(propagation=Propagation.REQUIRED, rollbackFor=Throwable.class)
public void activateSubdomainForSite(Long siteId, boolean activationOfSite)
So if there is a transaction open it should be propagated into this service. But here is what I don't get if that MySiteIdUpdate object isn't managed by spring does the first transaction move forward to the activateSubdomainForSite method ?? Or is another transaction being opened here. I looked in the logs and I believe it to be the latter but I rather ask the experts for a second oppinion before I proclame this legacy code to be complete rubbish to the project lead. I'm suffering with a StaleStateException somewhere further down the road and I'm hoping this has anything to do with it.
I think the code is correct, and the second #Transactional should reuse the existing transaction.
Because:
1) Spring Transaction handling is done either by Proxies or by AspectJ advices. If it is done by Proxies then it is required that MySiteIdUpdate invoke an instance that is injected (this is what you did). If you use AspectJ, then it should work anyway.
2) The association Transactions to the code that use is done by the Thread, this mean, as long as you "are" in the thread which started the transaction you can use it. (you do not start an new thread, so it should work)
An other way to explain: It is perfect legal when you have some method in your call hierarchy that does not belong to an spring bean. This should not make the transaction handling fail.

Spring #Transactional annotation behaves weird

I have the following method:
#Transactional
public void onEmailMessage() {
this.departmentService.removeUserFromDepartments(user, depsids);
this.departmentService.getDepartmentUsers(user.id);
}
The weird thing when i invoke this method, the first line:
this.departmentService.removeUserFromDepartments(user, depsids);
is called but the DB is not changing at all and the user is still connected to the deparment (many to many relation)
afterwards the method :
this.departmentService.getDepartmentUsers(user.id);
is called and returns users that are connected to the department including the removed user from line#1.
when the method returns - if i check the DB the user i removed is actually been removed from the table!
can i make the query return the actual updated values??
There is nothing weird about this. You are performing two different queries within the same transaction. Persistence context is updated, but the transaction hasn't been committed yet, and you can't see your changes after first line is finished. Transaction is a set of statements (in this case - statements created by those two methods of yours) which gets executed after commit is invoked. When the whole (onEmailMessage) method finished it's job, the transaction is committed and you are seeing the changes.
The solutions would be:
Make them as two separate transactions. For e.g:
#Transactional
public void removeUser(...) {
someInstance.departmentService.removeUserFromDepartments(user, depsids);
}
And:
#Transactional
public List<?> getUsers(...) {
return someInstance.departmentService.getDepartmentUsers(user.id);
}
Then the highest level would be onEmailMessage() method, which has to be non-transactional and in separate class then these two methods above. Call them both in this level and it will work.
You have marked it as Transactional. Changes in DB is made after executing all the queries. Either all of the operations will be committed or none.
The transaction hasn't been committed yet so changes won't necessarily have been written to the DB.
You could try calling
entityManager.flush();
after removeUserFromDepartments() but before getDepartmentUsers() to force the DB changes to be written before the commit.

Spring nested transaction marked as rollbackonly with unchecked exception

I am fairly new to Spring and transactions. I am sure this question has been asked before, but I still cannot figure the correct way to go about it.
I am using Spring and hibernate. I have a service method that goes like this:
#Transactional
public void processPendingReport(Report report) {
try {
// Do processing stuff, update report object state
reportDAO.save(report);
} catch (Exception e) {
reportDAO.markReportAsFailed(report);
}
}
If a RuntimeException occurs during processing, a "Transaction marked as rollbackOnly" RollbackException will be thrown, having as a result that the report will not be marked as failed (although I would like it to be).
I have tried using #Transactional(noRollbackFor=Exception.class), but still get the same issue.. Any suggestions? Could it be a configuration issue?
If a database exception (e.g. constraint violation) occurs in reportDAO.save() or reportDAO.markReportAsFailed() the transaction will be rolled back on the database level no matter what you are doing on the application level.
You can still mark the report as failed if reportDao.save() fails when you create a new transaction for reportDAO.markReportAsFailed(). Since ReportDAO is annotated #Transactional just remove the #Transactional annotation from the service method. You could also change the reportDAO.save() implementation to use a database function or stored procedure that wraps the insert statement and catches any exceptions on the database level.
HTH.

#Transactional Annotation + for a data insertion in a loop

I am using Spring 3, JPA + Hibernate for a CMS application. In that application I have a service class method which is annotated with #Transactional Annotation with rollBack property. Inside that method I am inserting data (ie entity classes) to a table using a loop. For each iteration of the loop entity classes has to be saved to the database. But it is not happening. The commit only happens when the execution of the loop has completed and exits from the method. Then it commits and saves all at once. But I need to read data once it gets inserted into the database before committing in this case. I tried with the ISOLATION LEVEL to read uncommitted but it didn't supported since I am using the default JPADialect. Also tried to add the hibernate implementation of jpaDialect but still it didn't worked. Please help with a workaround for this problem. One more thing, is there any way using propagation required method.
You are right, this is what I stands for in acid. Because the transactions are working in isolation, other transactions cannot see them before they are committed. But playing with isolation levels is a bad practice. I would rather advice you to run each and every iteration in a separate transaction with start and commit inside.
This is a bit tricky in Spring, but here is an example:
public void batch() {
for(...) {
insert(...)
}
}
//necessarily in a different class!
#Transactional
public void insert() {
}
Note that batch() is not annotated with #Transactional and insert() has to be in a different class (Spring service). Too long to comment, but that's life. If you don't like it, you can use TransactionTemplate manually.
remove the transactional annoation on the the method with loop.
In the loop call a separate method to perform the save, make that method transactional
You either need to go with programmatic transactions (Spring's TransactionTemplate or PlatformTransactionManager are the classes to look at, see Spring Doc for programmatic transactions, or you can call another transactional method from within your loop where the transaction is marked with Propagation.REQUIRES_NEW, meaning each call of that method is executed in its own transaction, see here. I think that the second approach requires you to define the REQUIRES_NEW method on a different Spring bean because of the AOP-Proxy. You can also omit the REQUIRES_NEW if the loop is not executed within a transaction.

Resources