I want to have a classic transactional behavior : create a, create b, if creation of b fail, rollback creation of a.
"Not so difficult, put an @Transactional spring annotation on your method."
So did I. But it doesn't work.
I'm using spring mvc 5.0.8 (Same version number for spring tx, context, in the below pom.xml). The database is mysql v.5.7.23
First, my config/code, then some redundant error many one get.
Here is a service-level method.
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public B createB(B b) {
//some logic to create a, instance of A
aDao.saveA(a);
//some logic to create b, instance of B
return bDao.saveB(b);
}
Here is the datasource, jdbctemplate, and transaction manager config :
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getRequiredProperty("jdbc.url"));
dataSource.setUsername(env.getRequiredProperty("jdbc.username"));
dataSource.setPassword(env.getRequiredProperty("jdbc.password"));
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
return jdbcTemplate;
}
@Bean("transactionManager")
public PlatformTransactionManager getTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
Here is a part of my pom.xml :
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
If you need more code, from Daos, or other, please ask.
Edit 1 : Add daos
@Repository
public class SqlADao implements ADao {
private static final String CREATE_A = "INSERT INTO a(id, param1,
param2) VALUES(?, ?, ?)";
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Addressee saveA(A a) {
try {
String id = UUID.randomUUID().toString();
a.setId(id);
jdbcTemplate.update(CREATE_A, id, a.getParam1(),
addressee.getParam2());
return a;
} catch (Exception e) {
throw new DaoException("Exception while accessing data.", e);
}
}
}
@Repository
public class SqlBDao implements BDao {
private static final String CREATE_B = "INSERT INTO b(id, param1,
param2, param3, param4, param5, param6) VALUES(?, ?, ?, ?, ?, ?, ?)";
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account saveAccount(Account account) {
try {
String id = UUID.randomUUID().toString();
b.setId(id);
jdbcTemplate.update(CREATE_B, id, b.getParam1(),
Date.valueOf(b.getParam2LocalDate()), b.getParam3,
b.getParam4(), b.getParam5(),b.getParam6());
return b;
} catch (Exception e) {
throw new DaoException("Exception while accessing data.", e);
}
}
}
End of Edit 1
Now, possible answer found on many place on the internet, mostly stackoverflow :
- Rollback work only for unchecked exceptions (=RuntimeException).
The BDao throws a custom RuntimeException. And I added the "rollbackFor = Exception.class" on the @Transactionnal definition. - Due to internal way @Transactional works (AOP), you have to call it from outside the method.
That is the case, the "createB" method is directly called from another class (at the moment, only from a controller". - You have to define a "transactionManager", with this precise name.
At first I had no transaction manager. When I realized, I felt a bit dumb. But even with one, with this precise name, it doesn't work. - I even found someone who needed to add the propagation=Propagation.REQUIRED, so I did too, but it doesn't change anything.
- The Database have to support transactions, rollback...
It's a mysql with InnoDB engine on all table, which according to same answers supports it.
Now, I don't have any other idea, and can't found other ideas on internet, so here I am.
RuntimeExceptionso shouldn't be needed. 3. If there is a singlePlatformTransactionManagerthe name doesn't matter. 4. That is the default. - M. Deinum@EnableTransactionManagementom your Configuration class? - M. Deinum