Hibernate - Spring - Avoid rollback - spring

I need to insert data in two different table. After insert in the first table (save1 function) I start an cycle for on array String where foreach element i call save2 function. In the save2 function maybe raised an ConstraintViolationException. I've annotaded method as can you see with Transacational.
I would that exception raised in method save2 not propagate rollback for save1 method. Is it possible?
#Service
#Transactional(propagation = Propagation.REQUIRED, value = "transactionManager")
public class MyService extends BaseService {
public void insert(AppFileForm appFileForm) {
try {
_insert(appFileForm);
setStatusOk();
} catch (IOException ioe) {
setStatusKo(msg.getProperty("file.upload.error"));
} catch (Exception e) {
e.printStackTrace();
setErrorFromException(e);
}
}
public void _insert(){
save1();
save2();
}
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor={Exception.class})
public void save1(){
for(String s: myArray){
save2();
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor={Exception.class})
public void save2(){
}
}
The dao method are irrilevant
public void save(MyClass mc) throws Exception{
em.persist(mc);
}

#Transactional annotation has no effect when called from another method of the same component.

Related

ControllerAdvice can not handle CompletionException

While experimenting with asynchronous processing, I discovered an unusual phenomenon.
#Slf4j
#ControllerAdvice
public class ErrorAdvice {
#ExceptionHandler(Exception.class)
public void ex(Exception e) {
log.info("error e = ", e);
}
#ExceptionHandler(CompletionException.class)
public void ex2(CompletionException e) {
log.info("error e = ", e);
}
}
#Service
#RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
#Transactional
public void save(String name) {
memberRepository.save(new Member(name));
throw new IllegalStateException();
}
}
public class Client {
#Transactional
public void save(String name) {
CompletableFuture.runAsync(() -> memberService.save(name));
}
}
Client starts a transaction and CompletableFuture starts another thread, thus starting a newly bound transaction scope.
But the problem is that I can't catch the error in ControllerAdvice. Now I think it's very risky in real application operation. What is the cause and how to fix it?
In my opinion, ControllerAdvice wraps the controller as a proxy, so even if Memberservice does asynchronous processing, it doesn't make sense that the exception is not handled because it is inside the proxy. It doesn't even leave any logs. Why is the exception being ignored??
why are you using CompletableFuture.runAsync(() -> memberService.save(name));?
just try memberService.save(name));

Rollback Operation not happening with #Transactional from Abstract Class

**Sample Code Snippet and use case**
Here I am extending abstract class and in abstract class only i have final method which has been marked with #Transactional, so upon any exception in any of the 3 methods implemented the transaction should rollback but its not happening.
public abstract class A<E e>{
public abstract void processData(String str);//process some data
public abstract E persistData(E e);//save some data based on E object
public abstract E sendData(E e);
#Transactional(readOnly=false,propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED,rollbackfor={all custom exception classes}
public final E processMethod(E e){
E e1;
processData(String str);
try{
e1= persistData(e);
}
catch(Exception e){
throw new RuntimeException();//or custom Exception
}
try{
e1= sendData(e1);**//Some Exception happens here but still data saved by persistData().**
}
catch(Exception e){
throw new RuntimeException();//or custom Exception
}
}
public class C extends A<Entity>{
#override
public void processData(){ //processing Data
}
#override
public Entity persistData(Entity e){
//saving data successfully
}
#override
public Entity sendData(Entity e){
//any Exception in sendData Method after persisting Data in Db is not rolling back, instead saving data in db upon exception also
}
}
**Sample Code only to follow use case**
#SpringBootApplication
#EnableTransactionManagement
public class Application{
C c= new C();
E e=new E();
e=c.processMethod(e);
}

Spring batch update db status after rollback due to exception

In Spring batch Writer I'm updating the db row status from 0 to 1. If any exception occurs update to 2.
However due to #transaction rollback I'm unable to update the status to 2.
(I'm throwing exception to trigger the rollback)
#Override
#Transactional
public void write(List<? extends TestEntity> enityList) throws Exception {
for(TestEntity testEntity : enityList) {
try {
testEntity.setStatus(2);
testRepository.save(testEntity);
testRepository.flush();
testMethod(testEntity); (which throws exception)
}catch (Exception exception) {
testEntity.setStatus(2);
testRepository.save(testEntity);
}
}
}
#Transactional
public void testMethod(TestEntity testEntity) throws Exception {
try{
//Some service call
//...
} catch(Exception e) {
log.error("error", e);
throw new Exception("exp");
}
}
Methods that have the #Transactional will rollback the transaction when they throw an exception. So if an exception is an expected and okay-ish flow of your code, you shouldn't throw an exception and return some kind of result object or status code instead.
#Transactional
public void testMethodThatIsAllowedToFail(TestEntity testEntity) {
try{
//Some service call
} catch(Exception e) {
return Status.FAILURE; // enum you have to create
}
return Status.SUCCESS;
}
// spring batch writer
public void write(List<? extends TestEntity> enityList) throws Exception {
[...]
Status result = testMethod(testEntity); (which throws exception);
if (result != Status.SUCCESS) {
// do something with it
}
[...]
}
you could also play around with #Transactional(propagation = Propagation.REQUIRES_NEW) but you would have to think hard whether having an extra transaction is desireable.

Aspect around methods annotated with #RabbitHandler

What I want is to have an aspect around all methods annotated with #RabbitHandler so that AssertionErrors won't kill the handler thread.
I just want to wrap them inside RuntimeExceptions and rethrow.
Motivation: there is additional error handling that I want to use which works well except for these AssertionErrors.
I could add a try-catch for AssertionErrors in each method but there are too many places and instead I was thinking of using aspects.
#Aspect
public class RabbitAssertionErrorHandlerAspect {
#Around("#annotation(org.springframework.amqp.rabbit.annotation.RabbitHandler)")
public Object intercept(ProceedingJoinPoint pjp) throws Throwable {
try {
return pjp.proceed();
} catch (AssertionError e) {
throw new RuntimeException(e);
}
}
}
All nice and elegant but it doesn't get called. I assume this has something to do with the way these methods are discovered in the first place.
Any reasonable workarounds anyone sees?
The #RabbitListener on the class with those #RabbitHandlers can be configured with:
/**
* Set an {#link org.springframework.amqp.rabbit.listener.RabbitListenerErrorHandler}
* to invoke if the listener method throws an exception.
* #return the error handler.
* #since 2.0
*/
String errorHandler() default "";
So, consider to go the custom RabbitListenerErrorHandler way instead.
It works with Spring AOP...
#SpringBootApplication
public class So48324210Application {
public static void main(String[] args) {
SpringApplication.run(So48324210Application.class, args);
}
#Bean
public MethodInterceptor interceptor() {
return i -> {
try {
System.out.println("here");
return i.proceed();
}
catch (AssertionError e) {
throw new RuntimeException(e);
}
};
}
#Bean
public static BeanNameAutoProxyCreator proxyCreator() {
BeanNameAutoProxyCreator pc = new BeanNameAutoProxyCreator();
pc.setBeanNames("foo");
pc.setInterceptorNames("interceptor");
return pc;
}
#Bean
public Foo foo() {
return new Foo();
}
public static class Foo {
#RabbitListener(queues = "one")
public void listen(Object in) {
System.out.println(in);
}
}
}
or, as Artem said, a custom error handler will work too.

How to rollback transaction in spring

I am facing a problem in transaction rollback using the #Transactional annotation.
I have the following methods in backingbean, service and dao classes:
public class ItemBackingBean {
public void saveUpdate() {
try {
ItemService.executeTransaction();
}
catch(Exception e) {
}
}
}
public class ItemService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeTransaction() {
deleteItem();
createOrder();
}
private void deleteItem() {
persist();
}
private void createOrder() {
persist();
}
private void persist() {
JpaDaoImpl.persist(object);
JpaDaoImpl.update(object);
}
}
public class JpaDaoImpl implements JpaDao {
#Transactional(readOnly = true)
public persist(Object object) {
getEm().persist(object);
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void update(Object object) {
getEm().merge(object);
}
#Transactional(readOnly = true)
public void remove(Object object) {
getEm().remove(object);
}
}
If any exception occurs in createOrder(), all transactions should rollback but it is not happening. Can any body tell me the problem?
What is the impact of #Transactional in JpaDaoImpl.java? The persist() and update() methods have different radOnly values. This Dao is existing code in our project and we don't want to change it. Can anybody help?
Regards,
Bandu
For those who don't want to throw exception (transaction should NOT be only rollbacked when exception happen), use this: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
If any exception occurs in createOrder(), all transactions should rollback but it is not happening. Can any body tell me the problem?
Rollback occurs only for RuntimeExceptions (see http://docs.spring.io/spring/docs/2.0.8/reference/transaction.html "please note that the Spring Framework's transaction infrastructure code will, by default, only mark a transaction for rollback in the case of runtime, unchecked exceptions;") but it is a customizable behaviour
You can keep the default transaction propagation that is PROPAGATION_REQUIRED without affecting the existing code if you want a ALL or NOTHING behaviour

Resources