Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data with JPA not rolling back transaction on error

We have Spring Data configured for JPA. A Service Transaction method doesn't get rolled back for an error (e.g. a DB ConstraintViolationException).

The closest I could find was this (Transaction not rolling back) Spring-data, JTA, JPA, Wildfly10 but we don't have any XML configuration, all of our configuration is Java-based.

Essentially, a service method looks like this: no errors are caught, everything thrown.

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = false)
public void insertEvent() throws Exception {
     // Part 1
     EventsT event = new EventsT(); 
     // populate it..
     eventsDAO.save(event);

     // Part 2 - ERROR HAPPENS HERE (Constraint Violation Exception)
     AnswersT answer = new AnswersT();
     // populate it..
     answersDAO.save(answer);   
}

Part 2 fails. But after the error and return, I see that the Event (Part 1) is still populated in the DB.

We also tried various combinations of @Transactional, nothing worked:

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = false)
@Transactional(readOnly = false)
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = ConstraintViolationException.class, readOnly = false)

Spring Data CRUD DAO Interfaces:

@Repository
public interface EventsDAO extends JpaRepository<EventsT, Integer> {

}

@Repository
public interface AnswersDAO extends JpaRepository<AnswersT, Integer> {

}

JpaConfig:

@Configuration
@EnableJpaRepositories(basePackages = "com.myapp.dao")
@PropertySource({ "file:${conf.dir}/myapp/db-connection.properties" })
public class JpaConfig {

    @Value("${jdbc.datasource}")
    private String dataSourceName;

    @Bean
    public Map<String, Object> jpaProperties() {
        Map<String, Object> props = new HashMap<String, Object>();
        props.put("hibernate.dialect", PostgreSQL95Dialect.class.getName());
        //props.put("hibernate.cache.provider_class", HashtableCacheProvider.class.getName());
        return props;
    }

    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setShowSql(true);
        hibernateJpaVendorAdapter.setGenerateDdl(true);
        hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
        return hibernateJpaVendorAdapter;
    }

    @Bean
    public PlatformTransactionManager transactionManager() throws NamingException {
        return new JpaTransactionManager( entityManagerFactory().getObject() );
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
        LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
        lef.setDataSource(dataSource());
        lef.setJpaPropertyMap(this.jpaProperties());
        lef.setJpaVendorAdapter(this.jpaVendorAdapter());
        lef.setPackagesToScan("com.myapp.domain", "com.myapp.dao");
        return lef;
    }

    @Bean
    public DataSource dataSource() throws NamingException {
        return (DataSource) new JndiTemplate().lookup(dataSourceName);
    }   

}

Have there been any transaction rollback issues with Spring Data & JPA?

like image 766
gene b. Avatar asked Dec 15 '17 18:12

gene b.


2 Answers

Believe it or not we fixed it. There were 2 parts to the solution:

1) Add @EnableTransactionManagement to JpaConfig as ledniov described, but that alone wasn't enough;

2) Also in JpaConfig in entityManagerFactory(), add the Service class package to the following setPackagesToScan. Previously, the domain object package was there, but the service object package was not. We added "myapp.service", the 2nd package.

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
    LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
    lef.setDataSource(dataSource());
    lef.setJpaPropertyMap(this.jpaProperties());
    lef.setJpaVendorAdapter(this.jpaVendorAdapter());
    lef.setPackagesToScan("myapp.domain", "myapp.service"); //NOTE: Service was missing
    return lef;
}
like image 62
gene b. Avatar answered Nov 18 '22 07:11

gene b.


You have to add @EnableTransactionManagement annotation to JpaConfig class in order to enable Spring's annotation-driven transaction management capability.

like image 40
ledniov Avatar answered Nov 18 '22 07:11

ledniov