简体   繁体   中英

@Transactional not working with Spring Boot and JDBC

My service should save data to a parent and child database tables, and rollback when an error ocurrs. I've tried forcing an error, using a hardcoded RuntimeException , and found the transaction gets commited no matter what. What I'm I missing? I'm using Spring Boot 2, including the spring-boot-starter-jdbc dependency.

Database is Oracle 11g.

Main configuration:

@SpringBootApplication
@EnableTransactionManagement
public class MyApplication extends SpringBootServletInitializer{

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MyApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

Service layer:

    @Service
    public class MyBean {

    private final NamedParameterJdbcTemplate jdbcTemplate;        
    private final MyDAO myDao;

    @Autowired
    public MyBean (NamedParameterJdbcTemplate jdbcTemplate, MyDAO myDao) {
        this.jdbcTemplate = jdbcTemplate;
        this.myDao= myDao;
    }

    @Override
    @Transactional
    public void saveData(...){
        myDao.saveData(jdbcTemplate, ...);
    }

}

DAO:

public void saveData(jdbcTemplate, ...){
    saveDataInParentDatatable(jdbcTemplate, ...);
    saveDataInChildDatatable(jdbcTemplate, ...);
}
private void saveDataInChildDatatable(jdbcTemplate, ...){
    throw new RuntimeException();
}

I faced a similar issue. I'm assuming that you were calling the MyBean.saveData method at MyBean's another method.

After searching, trying and failing a lot, I found this link: http://ignaciosuay.com/why-is-spring-ignoring-transactional/

In it, it's explained that when the invoked method is in the same class where is it invoked, the @Transactional annotation is ignored. The Spring explanation for it is:

“In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, ie @PostConstruct.”

So I created another class to encapsulate my DAO method calling, used its method instead and it worked.

So for this case, it could be something like:

MyBean:

@Service
public class MyBean {

    MyBean2 bean2;

    public void saveData(...){
        bean2.saveData(jdbcTemplate, ...);
    }
}

MyBean2:

@Service
public class MyBean2 {

    private final NamedParameterJdbcTemplate jdbcTemplate;        
    private final MyDAO myDao;

    @Autowired
    public MyBean2 (NamedParameterJdbcTemplate jdbcTemplate, MyDAO myDao) {
        this.jdbcTemplate = jdbcTemplate;
        this.myDao= myDao;
    }

    @Override
    @Transactional
    public void saveData(...){
        myDao.saveData(jdbcTemplate, ...);
    }
}

尝试这个:

@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)

推荐使用:

@Transactional(rollbackFor = {Exception.class, RuntimeException.class})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM