**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);
}
Related
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));
I'm unable to rollback a transaction in Spring Boot 2.1.4 involving two DAOs (JDBC). The DAOs implement a parameterized interface. The first DAO inserts a record into three tables, and the last DAO inserts a record into one table. I'm forcing the last DAO insert to fail.
Database is DB2.
Some code:
public interface FooDao<T extends Foo> {
int insert(T foo) throws SQLException;
}
#Repository
public class FooDaoJdbc implements FooDao {
#Override
public int insert(Foo foo) throws SQLException {
insertFoo(foo);
insertFooDescriptions(foo);
insertFooActivity(foo);
return 0;
}
}
#Repository
public class BazDaoJdbc implements FooDao<Baz> {
#Override
public int insert(Baz baz) throws SQLException {
public interface FooService<T extends Foo> {
void add(T foo) throws SQLException;
}
#Service
public class BazServiceImpl implements FooService<Baz> {
private FooDao<Baz> bazDaoJdbc;
private FooDao<Foo> fooDaoJdbc;
#Transactional(rollbackFor = Exception.class)
public void add(Baz baz) throws SQLException {
fooDaoJdbc.insert(foo);
bazDaoJdbc.insert(baz);
}
}
It sure seems the FooDaoJdbc and BazDaoJdbc are in the same transaction when I debug TransactionAspectSupport, but the rollback seems to ignore FooDaoJdbc. I don't have a record in the "baz" table, but I would expect the three "foo" tables to rollback, too.
My eyes are crossed at this point. Do I have a misplaced annotation? Am I making things too "fancy" with generics? Thanks in advance for the help!
A #Transactional method only rollback when unchecked exception is thrown. So you have to throw RuntimeException in add method when an exception is thrown.
#Service
public class BazServiceImpl implements FooService<Baz> {
private FooDao<Baz> bazDaoJdbc;
private FooDao<Foo> fooDaoJdbc;
#Transactional
public void add(Baz baz) {
try {
fooDaoJdbc.insert(foo);
bazDaoJdbc.insert(baz);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
More information:
https://www.catalysts.cc/wissenswertes/spring-transactional-rollback-on-checked-exceptions/
I have the following scenario.
I have one transaction method which calls another transaction method which having REQUIRED_NEW Propagation. if the first method gets exception then the second method (REQUIRED_NEW Propagation) also rollbacks.
I am using JPA, Spring-boot and chainedKakfkaTransactionManager
I have tried with changing chainedKakfkaTransactionManager to default one still no luck
here is my code :
#Service
#Transactional(readOnly = false)
public class ComponentServiceImpl implements ComponentService {
#Autowired
private UserRepository userRepository ;
#Override
#Transactional
public boolean validateName(String name) {
try{
retrun userRepository.validate(name);
}catch(Exception e){
handleError(name);
throw new Exception("user not valid");
}
}
#Override
#Transactional(propagation=Propagation.REQUIRES_NEW)
public boolean handleError(String name) {
userRepository.update(name);
}
}
Rollback is happening in the handleError method too. is there any code mistake?
Thanks #DarrenForsythe,
By Creating an autowire object for the same class (self-reference) its worked for me
#Service
#Transactional(readOnly = false)
public class ComponentServiceImpl implements ComponentService {
#Autowired
private UserRepository userRepository ;
// CREATE SELF REFRENCE
#Autowired
private ComponentService componentService;
#Override
#Transactional
public boolean validateName(String name) {
try{
retrun userRepository.validate(name);
}catch(Exception e){
componentService.handleError(name);
throw new Exception("user not valid");
}
}
#Override
#Transactional(propagation=Propagation.REQUIRES_NEW)
public boolean handleError(String name) {
userRepository.update(name);
}
}
Class A{
#Autowired
private B b;
#Transactional(rollbackFor = Exception.class)
public void run(){
try{
b.insert1();
b.insert2();
b.sendFile();
}catch(Exception e){
//Exception block
}
}
}
Class B{
public void insert1(){//db insert happens here}
public void insert2(){//db insert happens here}
public void sendFile() throws Exception{
}
}
In the above case rollback works perfectly if insert2 method fails.
But if insert1 and insert2 are completed and sendFile has an exception, rollback is not happening.
Please help me for this. I am stuck with this for the last 2 days.
Thanks
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.