Spring Boot: Transaction silently rolled back because it has been marked as rollback-only - spring-boot

I am trying to save exception message in DB table in Catch block but the Exception Transaction is also getting rolled back when the code passes the whole #Transactional method.
Ex:
#javax.transaction.Transactional
public void operationMethod(){
serviceMethod();
}
public void serviceMethod()
try{
}
catch(Exception e){
saveException(e.getMessage());
}
}
//here tried #org.springframework.transaction.annotation.Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class) and different combinations with Nested and Required
saveException(String exceptionMessage){
//save it into table logic
}
Still it is giving the "Transaction silently rolled back because it has been marked as rollback-only" exception. Is there any way to force commit only saveException part without save try block and rolling back exception save.
Thanks in Advance

Related

Failure in inner #Transactional(readOnly = false, rollbackFor = Exception.class) rolls back entire outside persistence

I have a job method in a class-annotated #Transactional. This job method calls inner methods for persistence of individual records. If I simulate an error in the following inner update() method somewhere in the middle of my result set processing, I see that all successful records before/after this exception do not get saved after job completion. Why is that? All outside persistence should remain, with the exception of the individual record that failed. The inner update alone has rollbackFor.
#Service("mailService")
#Transactional
#EnableScheduling
public class MailServiceImpl implements MailService {
#Override
#Scheduled(cron = "${mail.cron.pubmed.autosynch.job}")
public void autoSynchPubMedJob() {
//... Fetch result set
for (Result r: resultset) {
try {
pubService.updatePublication(r);
} catch (Exception e) {
// Silently log and continue
log.error("Error on record: ", e);
}
}
}
The updatePublication method, this is the one with rollbackFor:
#Override
#Transactional(readOnly = false, rollbackFor = Exception.class)
public void updatePublication(Publication publication) throws Exception {
dao.update1(..);
dao.update2(..);
// Simulate exception for a specific record for testing
if (publication.getId() == 123) {
throw new Exception("Test Exception");
}
}
Result: no successful data persisted at all at the end of job completion. There should be partial persistence (for other successful records).
When I remove this Exception simulation, all data is successfully persisted at the end. Also, all data is persisted if I remove the inner call's rollbackFor.
Probaby because it uses existing transaction. Try opening a new one with propagation = REQUIRES_NEW.
Note: New transaction won't be opened if you call the method from the same service. You should use either self-reference call or extract logic to another #Service.

Transaction propagation in spring #required

#GetMapping("trans")
#Transactional()
public String primaryTrans() {
User u1 = new User(0,"test","test#email.com");
us.save(u1);
User u2 = new User(0,"test1","test1#email.com");
us.save(u2);
secondaryTrans();
return "index";
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
private void secondaryTrans() {
// TODO Auto-generated method stub
User u2 = new User(0,"test2","test3#email.com".repeat(300));
us.save(u2);
}
Here i am manually raising DATA TOO LONG exception from secondary transaction, But it causes primary transaction also rolled back. How can we make sure that primary transaction to be committed irrespective of secondary transaction
In this case, since the second method is called from the same class, the second transaction is most likely not created. Springs transactional support uses AOP proxies to create transactions. The docs contain a description on why this will not work.
The simplest way is to catch the exception thrown from secondaryTrans() method, so just wrap secondaryTrans() into try-catch block:
try {
secondaryTrans();
} catch (Exception e) {
//...
}

Spring #Transactional - how to avoid specific exception from rolling back?

I have the following method:
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void applyLog(int codFilial, List<LogLojaCentralCompactoEntity> items) {
}
which internally calls:
#Override
#Transactional(noRollbackFor = PersistenceException.class)
public void apply(LogCompactoEntity entity) {
}
The second method has a try/catch a PersistenceException. The problem is the transaction rolls back then it reaches PersistenceException.
I know Spring #Transactional defaults to roll back in any unchecked exception, but I am explicitly telling noRollbackFor the PersistenceException.
Why its not working? Any way to threat it?
Thanks.
Edit - the try/catch method inside apply does this:
try {
insert();
}
catch(PersistenceException e)
{
update();
}
Edit2 - log:
Edit3 - exception handling part:
if (acao.equalsIgnoreCase("I")) {
try {
insertQuery.executeUpdate();
}
catch(PersistenceException e) {
int affected = updateQuery.executeUpdate();
if(affected == 0)
throw new LogApplyException("O insert falhou e o update não afetou registros.");
}
}
Edit4 - Some more info:
1) Removing #Transactional from apply, leaving #Transaction just on applyLog results on this exception:
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute statement
2) Same as 1, but adding noRollbackFor = PersistenceException.class (on applyLog) results on this exception:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
Edit 5:
#lzagkaretos solution is valid (Thank you), but I want to know how to handle this for future cases.
UPDATE
I think that relying in primary key violation exception in order to find if record should be inserted or updated is not something you should do. Another implementation you can use instead is finding before the execution if record is already saved in the database to perform an update, or not to perform an insert.
For example, if you can use spring data repositories, the code might seem like this.
public void apply(LogCompactoEntity entity) {
LogCompactoEntity logCompactoEntity = (entity.getId() == null) ? new LogCompactoEntity() : logCompactoRepository.findOne(entity.getId());
if (logCompactoEntity == null) {
logCompactoEntity = new LogCompactoEntity();
}
copyProperties(entity, logCompactoEntity);
logCompactoRepository.save(logCompactoEntity);
}
--
Maybe you shouldn't have a try/catch block in apply method and declare it with throws PersistenceException in method signature.
In order for noRollbackFor to work, PersistenceException has to be thrown from apply method. You can try the following.
try {
insert();
}
catch(PersistenceException e) {
update();
throw e;
}

Why Grails/Spring transactional behavior not working in this case?

I have a grails (2.5.2) app, with a mysql and a NoSQL interaction. There's a main/principal service method that call 2 other methods:
class mainService {
static transactional = false
NoSQLDataAccessService noSQLDataAccessService
// main/principal method
#Transactional
void save(json){
// (1) creating domain entities from json
addNewDomainEntities(entities)
// (2)
noSQLDataAccessService.set(json)
}
#Transactional
void addNewDomainEntities(entities){
// save the entities in a mysql schema and use save(flush:true)
// because i need the generated id's
}
}
As you can see, this mainService creates new domain entities (1), flushing the session to get the id's. Then, i call other service method (2) that store the json in a NoSQL schema:
class NoSQLDataAccessService(){
static transactional = false
void set(json){
try{
// save the json in a NoSQL schema
} catch(Exception ex){
// if fails, i log the exception and throws it again
throws ex
}
}
}
But, sometimes the noSQLDataAccessService.set() fails by external causes and the entities created before still persist in the mysql db.(this is the problem)
The save method, that contains all this program execution, is marked like #Transactional, so if the noSQLDataAccessService.set() throws an exception, all the changes made should be not applied because the rollback. I'm right?
You probably have to throw an RuntimeException, not an Exception, to force a rollback as per this StackOverflow conversation. Instead of:
throws ex
you might try:
throw new RuntimeException(ex)
Further, I would recommend you be explicit about your transaction isolation. Perhaps something like:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE)

Can't catch StaleObjectStateException with hibernate

I'm having some trouble trying to catch an exception when there are concurrency violations using hibernate and Spring AOP. This is my scenario:
(My MyConcurrentStateControl has a #Version column)
Service layer
#Override
public Integer saveWork(WorkDto dto) throws MyException {
try {
return workBusinessLogicService.saveWork(dto);
} catch (PersistenceException ex) {
// -- Concurrent insert exception
if (ex.getCause() instanceof ConstraintViolationException) {
String errorMsg = "The same item is being created by another user. Please refresh.";
LOGGER.error(errorMsg, ex);
throw new MyException(errorMsg);
}
} catch (StaleObjectStateException ole){
// -- Concurrent update exception
String errorMsg = "The same item is being updated by another user. Please refresh";
LOGGER.error(errorMsg, ole);
throw new MyException(errorMsg);
}
return -1;
}
Business Logic layer
#Override
#Transactional(rollbackFor = {Exception.class, MyException.class, PersistenceException.class, StaleObjectStateException.class})
public Integer saveWork(WorkDto dto) throws MyException, PersistenceException, StaleObjectStateException {
MyConcurrentStateControl concurrentState = concurrentStateManager.getState(dto.getId());
if (concurrentState == null) {
concurrentState = new MyConcurrentStateControl();
}
// -- Do updates in some other tables --
// Save Concurrent State for concurrency check (Optimistic Locking)
Integer id = concurrentStateManager.save(concurrentState);
// -- Also tried entityManager.flush();
}
This is the error I'm getting in the log, and it is thrown in this line:
return workBusinessLogicService.saveWork(dto);
It is the error I'm expecting when multiple threads call the service, but I can't do anything with it.
[#|2015-01-30T20:53:49.793+0000|WARNING|glassfish3.1.2|javax.enterprise.system.core.transaction.com.sun.jts.jta|_ThreadID=32;_ThreadName=Thread-12;|JTS5054: Unexpected error occurred in after completion
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.mymodel.entity.ConcurrentState#7]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2471)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3123)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3021)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3350)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)
......................
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1012)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at net.bull.javamelody.MonitoringSpringInterceptor.invoke(MonitoringSpringInterceptor.java:74)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.mymodel.business.core.service.WorkServiceImpl$$EnhancerByCGLIB$$b1800d24.saveWork(<generated>)
I know the actual queries are triggered on transaction commit, and at that point the control has moved out of the method, therefore I'm not being able to catch the StaleObjectStateException. But how can I do that, or is there some alternative? All I want is:
Roll back all transactions.
Show a reasonable message to the user that there have been concurrent updates and he needs to refresh the UI.

Resources