summerbulb summerbulb - 5 days ago 9
MySQL Question

Spring @Transactional not rolling back

I'm using Spring's

NamedParameterJdbcTemplate
and I'm trying to use
@Transactional
to make sure that two inserts are either both persisted to the database or one is rolled back, in case the other one fails.

The code is designed to run with MySql and is being tested with H2 in memory.

It's not working... The second insert fails but the first is not rolled back.

Here are the relevant classes:

MySpringConfig.java

package com.MyPackage.spring

@Configuration
@ComponentScan({ "com.MyPackage" })
public class MySpringConfig {

@Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate() throws Exception {
return new NamedParameterJdbcTemplate(dataSource());
}

@Bean
public DataSourceTransactionManager dataSourceTransactionManager() throws Exception {
return new DataSourceTransactionManager(dataSource());
}


@Bean(name = "customerEntitiesDataSource")
public DataSource dataSource() throws Exception {
Properties properties = new Properties();
properties.put("url", "db-url");
properties.put("username", "uName");
properties.put("password", "pwd");
BasicDataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
return dataSource;
}


}

MyDao.java

package com.MyPackage.dao

@Repository
public class MyDao {
private ObjectMapper mapper = new ObjectMapper();

@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;

private static String INSERT_ELEMENT_QUERY = "INSERT INTO TableA..." + " VALUES (:param1, :param2)";

@Transactional(rollbackFor = {Exception.class}, propagation=Propagation.REQUIRED)
public void storeElements(List<Element> element) {


Stream<HashMap<String, Object>> hashMapStream = elements.stream().map(
element -> {
HashMap<String, Object> params = new HashMap<>();
params.put("param1", element.getParam1());
params.put("param2", element.getParam2());
return params;
}
);
List<Map<String, Object>> collect = hashMapStream.collect(Collectors.<Map<String, Object>>toList());
Map<String, Object>[] batchValues = new Map[]{};
batchValues = collect.toArray(batchValues);

jdbcTemplate.batchUpdate(INSERT_ELEMENT_QUERY, batchValues);

computers.stream().forEach(element -> saveExtraData(element));
}


private static String INSERT_ELEMENT_EXTRA_DATA_QUERY = "INSERT INTO TableB... Values (:val1, :val2)";

@Transactional(rollbackFor = {Exception.class}, propagation=Propagation.REQUIRED)
public void saveExtraData(Element element) {

Stream<HashMap<String, Object>> hashMapStream = element.getExtraData().stream().map(
extra -> {
HashMap<String, Object> params = new HashMap<>();
params.put("val1", extra.getVal1());
params.put("val2", extra.getVal2());
return params;
}
);
List<Map<String, Object>> collect = hashMapStream.collect(Collectors.<Map<String, Object>>toList());
Map<String, Object>[] batchValues = new Map[]{};
batchValues = collect.toArray(batchValues);

jdbcTemplate.batchUpdate(INSERT_ELEMENT_EXTRA_DATA_QUERY , batchValues);
}
}


The test I'm running is: Send a list of Elements to
storeElements()
but make sure that one of the elements has an
extraData
that is too long to be stored in
TableB
. I then check the database to make sure that no element was stored in the database.

The insert fails, but there is no rollback.

The whole code is a Spring stack, from the test, all the way through to the
storeElements
method.

Running debug, I see that none of the DataSourceTransactionManager methods are called, apart from the constructor, upon it's creation when the Spring context is loaded.

What am I missing here?

Answer

Annotate your MySpringConfig with @EnableTransactionManagement to enable transaction management.

@Configuration
@ComponentScan({ "com.MyPackage" })
@EnableTransactionManagement
public class MySpringConfig {
Comments