I have a scheduler code(In grails) which runs inside transaction block:
Plan.withTransaction{
.....
plan.save(failOnError: true)
transaction.save(failOnError: true)
try{
smsService.sendSMS(transaction) // transactional service
}catch(Exception e){
}
..........
}
Inside the sendSMS method:
def sendSMS(Transaction transaction){
try{
getSMSContent()
sendSms()
} catch(Exception e){ print ...exception catched here.... }
}
String getSMSContent(...){
..... it throws exception in some cases
}
Now the issue is that plan and transaction is not saved(validate returns ok with no errors) if getSMSContent() throws exception. I think the transaction should not roll back because we catched the exception in the same service. Do I need to catch the exception in the same method which is getSMSContent so that transaction does not rollback and plan/transaction is saved even if sms is failed ?
Or should I mark the sms service as "Requires_New" so that new transaction gets created ?
Related
I have the following method:
#Transactional
public Store handle(Command command) {
Store store= mapper.map(command.getStoreDto(), Store.class);
Store persistedStore = storeService.save(store);
addressService.saveStoreAddress(store, command.getEmployeeId()); //this method is not crucial, should be called independently and in another transaction, without any rollback in case of exception
return persistedStore;
}
addressService.saveStoreAddress is not crucial - when this method will throw any exception, store should be saved anyway (storeService.save(store);). What is the best solution in my case?
Use #Transactional(propagation=REQUIRES_NEW) on the saveStoreAddress() such that it will execute in a new and separate transaction.
To prevent the transaction of the handle() will be rollback because of the exception throw from saveStoreAddress() , you also have to try-catch when calling saveStoreAddress().
In the end , it looks something like:
#Service
public class AddressService {
#Transactional(propagation=REQUIRES_NEW)
public void saveStoreAdress(.....){
}
}
#Transactional
public Store handle(Command command) {
.......
try{
addressService.saveStoreAddress(store, command.getEmployeeId());
}catch (Exception ex){
/***
* handle the exception thrown from saveStoreAddress.
* If you want the current transaction not rollback just because of the
* exception throw from saveStoreAddress(), do not re-throw the exception when
* handling this exception
*/
}
return ....;
}
Spring 4.1.4
Hibernate 4.2.0
JDK 1.8
My context: I have a Controller calling --> Service --> calling Dao
The business funcionality is to delete ( in 1 to many DB relation) some child ,but not all child .
Then ,after deleting some child I try to delete the Parent and offcourse I got java.sql.SQLIntegrityConstraintViolationException
But the question is why transaction is market for Rollback ? ( in other words why I don't got the deletion of some child ?)
SQLIntegrityConstraintViolationException is a checked exception and stating Spring documentation the behaviour would be the same of EJB : Note that by default, rollback happens for runtime, unchecked exceptions only. The checked exception does not trigger a rollback of the transaction.
I need to remove some child anf trying to remove the parent if possible, if not I need to commit the transaction maintaining the parent and remaining of childs
Note I tried also to specify in Service and Dao methods the Spring Annotation
#Transactional(noRollbackFor = SQLIntegrityConstraintViolationException.class)
To request explicitly the behaviour expected , but not even like this work for me
Controller code method:
public void delete() {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Data deleted.","");
try{
memoTemplateService.delete(memoTemplate);
memoTemplates.remove(memoTemplate);
}
catch (Exception e){
msg=new FacesMessage(FacesMessage.SEVERITY_WARN, "A","B");
}
reset();
FacesContext.getCurrentInstance().addMessage(null, msg);
}
Service method :
#Override
#Transactional(noRollbackFor = {SQLIntegrityConstraintViolationException.class,DBConstraintException.class})
public void delete(MemoTemplate memoTemplate)throws BusinessException {
// deleting some ,not all , child
phaseAndMemoGenerator.deleteMemosForVisibleTimeHorizon(memoTemplate);
try{// some times Template cannot be deleted
memoTemplateDao.delete(memoTemplate);
}
catch (Exception e){
throw new DBConstraintException("Partial Delete", "Template cannot be deleted, Memo in the past are present");
}
}
Dao
#Repository(value = "memoTemplateDao")
public class MemoTemplateDaoImpl extends GenericJpaDaoImpl<MemoTemplate, Long> implements MemoTemplateDao {
#Override
#Transactional(noRollbackFor = SQLIntegrityConstraintViolationException.class)
public void delete(MemoTemplate t) {
super.delete(t);
em.flush();
}
}
Just an Update : it's incredible but I can't catch neither doing the catch in Dao method ,debugger go in catch block but before this still a java.sql.SQLIntegrityConstraintViolationException is fired , incredible !
#Transactional(noRollbackFor = {SQLIntegrityConstraintViolationException.class,PersistenceException.class})
public void tryToDelete(MemoTemplate t)throws Exception {
super.delete(t);
try{
em.flush();
}
catch (Exception e){
throw new Exception("ddddd");
}
}
If there are constraints defined in DB, you won't be able to bypass them by committing without rollback.
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
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.
My Grails Service calls a plugin which throws a runtime exception. In my case, I don't care about the exception, so it is swallowed.
MyGrailsService {
def myMethod {
...
try {
//callPlugin
} catch (Exception ex) {
...
}
}
}
All fine, exception is caught and processing continues. However, in my Controller, I have a catch (Throwable t) block, which I am not expecting to get executed because the exception is swallowed. It turns out the catch (Throwable t) block is executed because Grails throws a a org.springframework.transaction.UnexpectedRollbackException
which of course I do not want it to do. I guess I am getting this because the exception the plugin throws is runtime, so Grails rolls back the Transaction.
I don't want this UnexpectedRollbackException being thrown.
Any tips?
What i would have done is
class MyService{
static transactional = false
def myMethod {
...
try {
//callPlugin
} catch (Exception ex) {
...
}
}
#Transactional(readOnly = true)
def someMethod {
// Some code here
}
}
The above code will make all the methods in the service non transactional and we will explicitly make the methods transactional which we want.
However once more thing can be a point to be taken care of, the method of the plugin you are calling, that method can itself be a transactional method, which on error may get rolled back and throw a UnexpectedRollbackException. So you have to check it once if the plugin method is transactional.