Spring nested transaction marked as rollbackonly with unchecked exception - spring

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.

Related

Spring Boot: Rollback transaction before request resolves

I've got a method in my service class which performs some changes to an entity, afterwards it executes some critical code (communication to different microservice) which may come back with error information attached to an exception. If an error occurs, the changes should be reverted. To achieve this I thought I'd use #Transactional.
Service
#Service
public class MyService {
#Autowired
private EntityRepository repository;
#Transactional(rollbackFor = CriticalCodeException.class, isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED)
public Entity foo(entity) {
/* perform changes ... */
entity.setBar(...)
try {
/* critical code */
} catch (CriticalCodeException e) {
e.setEntityId(entity.getId());
throw e;
}
}
public attachError(CriticalCodeException e) {
// reload entity which should be rolled back but is not
Entity reloaded = repository.findOne(e.getEntityId);
reloaded.setError(e.getError); // attach error
repository.save(reloaded) // saves changes which should be rolled back
}
}
As the CriticalCodeException is a RuntimeException and might also occur in another context, I've created a designated ExceptionHandler which is supposed to catch it and attach the error to my entity.
ExceptionHandler
#ExceptionHandler(CriticalCodeException .class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ErrorResponse processError(CriticalCodeException e) {
entityService.attachError(e);
return e.getError();
}
Yet, when I attach the error I do not wont to save the pervious made changes too. So when I reload the entity after the Exception has been thrown out of my nested #Transactional-method I'd expect it to be in its initial state. Well, it is not. If I do not attach the error (and thus omit my save() call) the changes do not get persisted. So I figure the rollback eventually executes after my exception handling.
Can anybody tell me how I can access my entity in its initial state AFTER the rollback is done?
I am using Spring Boot Data with JPA and Hibernate. I've tried various configurations with things like #EnableTransactionManagement and setting up different combinations of beans. Yet, after a lot of research, I'm pretty confindent that the #Transactional is actually working (as to be seen from the eventual rollback described above) and should work without further configuration. What am I doing wrong?
EDIT
The changes were in fact rolled back but my entity-manager did not get reset, just M. Deinum pointed out. In my persistence setup changes to an entity were only persisted by a call to repository.save(entity). What I did eventually was to omit the transactional approach and instead implement repository.reload(entity) which refreshes my entity via the entity-manager. This way I can get rid of previous made changes which did not even get persisted in the first place.

Rollback is not working with spring declarative

I'm working with spring declarative approach to rollback transaction on any exceptions with in the method. This method had multiple DAO calls[1,2,3..] and those needs to be maintained in a single transaction. So i'm trying to acheive if any exceptions comes in DAO call[3], then spring has to rollback preceeding DAO calls. i.e[1,2]
#Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true,rollbackFor=java.lang.Throwable.class)
public void processWorkflowActionsInOneTransaction(...) throws Exception {
// DAO call 1 here....
// DAO call 2 here....
// DAO call 3 here....[throw exception]
}
The above configuration created a single transaction for the mentioned method, but it is not triggered rollback after exception. I have attached spring logs for the better understanding. Please help me if any one had a similar issue.
Spring logs

Spring Database Integration Test, when to flush or?

I am fairly new to spring, and doing some integration tests.
Using Hibernate, MySql and Spring data JPA.
I am using transaction support and everything gets rolled back at the end of each test.
For example:
#Test (expected=DataIntegrityViolationException.class)
public void findAndDelete() {
UUID uuid = UUID.fromString(TESTID);
User user= iUserService.findOne(uuid);
iUserService.delete(cashBox);
iUserService.flush();
assertNull(iUserService.findOne(uuid));
}
In the above code, I call the iUserService.flush(), so that the sql gets sent to the DB, and an expected DataIntegrityViolationException occurs because there is a foreign key from User to another table (Cascade is not allowed, None). All good so far.
Now, if I remove the iUserService.flush()
then the expected exception does not occur because the sql does not get sent to the DB.
I tried adding the flush() into a teardown #After method, but that didn't work as the test does not see the exception outside of the test method.
Is there any way to avoid calling the flush within the test methods?
It would be preferable if the developers on my team did not have to use the flush method at all in their testing code
Edit:
I tried adding the following
#Before
public void before() {
Session session = entityManagerFactory.createEntityManager().unwrap(Session.class);
session.setFlushMode(FlushMode.ALWAYS);
}
but it does seem to flush the sqls, before each query.
In my humble opinion, it's better than the developers of your team know what they are doing.
It includes the things that are configured by default and the consequences of that.
Please, take a look to why you need avoid false positives when testing ORM code

Spring transaction propagation_required issue

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.

Another LazyInitializationException (in combination with Spring+GSON)

I guess I'm another newbie guy who fails to understand Hibernate sessions, may be Spring's TransactionTemplate, dunno. Here's my story.
I'm using Hibernate 3.5.5-Final, Spring 3.0.4.RELEASE, trying to live only with annotations (for Hibernate as well as Spring MVC).
My first try was to use #Transactional annotations in combination with properly setup transaction manager. Seemed to work at first, but in long run (about 36hours) I started to receive "LazyInitializationExceptions" over and over again (from places that were running just fine in previous hours!).
So I switched to manual transactions using Spring TransactionTemplate.
Basically I'm having something like this protected stuff in my BaseService
#Autowired
protected HibernateTransactionManager transactionManager;
protected void inTransaction(final Runnable runnable) {
TransactionTemplate transaction = new TransactionTemplate(transactionManager);
transaction.execute(new TransactionCallback<Boolean>() {
#Override
public Boolean doInTransaction(TransactionStatus status) {
try {
runnable.run();
return true;
} catch (Exception e) {
status.setRollbackOnly();
log.error("Exception in transaction.", e));
throw new RuntimeException("Exception in transaction.", e);
}
}
});
}
And using this method from the service's impls worked OK, I did not see LazyInitializationException for 10 days (running Tomcat with this single app 24*7 for 10days, no restarts) ... but than hoops! It has popped again :-/
The LazyInitializationException comes from place under "inTransaction" method and there is no "inTransaction recursion" involved, so I'm pretty sure I should be in the same transaction alas in the same Hibernate session. There is no "data from previous session" involved (as far as my code goes that service layer opens the transaction, gathers all data from Hibernate it needs, process it and returns some result == service does not recall other top-services)
I have not profiled my app (I don't even know how to do that properly in long runs such as 10 days), but my only guess is that I'm leaking memory somewhere and JVM hits heap-limit...
Is there some "SoftReferences" involed inside Spring or Hibernat? I don't know...
Another funny thing is that the exception always happen when I try to serialize the result into JSON using Google GSON serializer. I know, it does not use getters ... I have my own patched version that is using getters instead of actual fields (so I'm making sure not to bypass Hibernate proxy mechanisms), do you think it may play some role here?
Last funny thing is that the exception is always happing in the single service method (not anywhere else), which is driving me nuts because this method is as simple-stupid as it could be (no extrem DB operations, just loads data and serialize them to JSON using lazy-fetching), huh???
Does anybody have any suggestions what should I try? Do you have some similar experiences?
Thanks,
Jakub

Resources