I have looked at several examples and don't understand what I am doing differently; however, this is not working for me. Here is a simplified description of my code, with code snippets, to illustrate what is not working.
I have a main transaction table that hold one record for each application transaction. I have a linked table that holds images from the transactions with a foreign key pointing back to the master transaction record. Nothing too strange or fancy here. (Full disclosure: There are two other linked tables, but I am certain that the fix for one table will work for all of them.)
My code creates a RESTful endpoint like this:
@Controller
@RequestMapping("/feed/v1")
public class ReportingDataFeedControllerV1 {
@Resource(name = "dataFeedService")
private DataFeedService dataFeedService;
@PostMapping(value = "/myend", consumes = "application/json", produces = "application/json")
public @ResponseBody DataFeedResponse DataFeed(@RequestBody DataFeedRequest request) {
DataFeedResponse response = new DataFeedResponse();
try {
dataFeedService.saveData(request);
DataFeedService is an interface that defines a saveData method and gets implemented by DataFeedServiceImpl:
public class DataFeedServiceImpl implements DataFeedService {
@Resource(name = "dataFeedRepository")
private DataFeedRepository dataFeedRepository;
@Override
//@Transactional // Outer @Transactional commented
/**
* Call the save method on the repository implementation class.
*
* @param request - DataFeedRequest object that contains the data to be saved
* @throws Exception
* @author SmithDE
*/
public void saveData(DataFeedRequest request) throws Exception {
dataFeedRepository.saveData(request);
}
DataFeedRepository is another interface that gets implemented by DataFeedRepositoryImpl:
public class DataFeedRepositoryImpl implements DataFeedRepository {
private static final int REF_SS_MFA = 4;
@Resource(name="rdpJdbcTemplate")
private JdbcTemplate jdbcTemplate;
So, there are at least two levels of classes between the method that starts this action and the real call to saveData().
My method, in class DataFeedRepositoryImpl, that saves transaction data to the database tables is annotated with @Transactional, like this: (It also includes @Override because it is the implementation of a interface from a base class.)
@Override
@Transactional // Nested @Transactional
/**
* Method that performs the save of the data to the database tables.
*
* @param request - Request object containing the data to be saved
* @exception Exception
* @author SmithDE
*/
public void saveData(DataFeedRequest request) throws Exception {
The method builds an INSERT statement for the main transaction table. It needs to retrieve the new primary key, so it calls the update method of JdbcTemplate like this:
KeyHolder keyHolder = new GeneratedKeyHolder();
int newRowCount = jdbcTemplate.update(connection -> {
PreparedStatement ps = connection
.prepareStatement(thisquery, Statement.RETURN_GENERATED_KEYS);
return ps;
}, keyHolder);
id = (long) keyHolder.getKey();
Next it builds a series of INSERT statements for the images associated with this transaction and sends them to the database in separate calls to JdbcTemplate.update(), like this:
for (DataFeedImage image : images) {
...
newRowCount = jdbcTemplate.update(thisquery);
}
My expectation is that an error in any of the calls to insert images into the linked image table will cause the entire database transaction to rollback. However, my observation is different:
- The main transaction data record is still there in the main transaction table.
- Any images that were inserted before the error are still there in the image table.
I want all of these calls to update() to be part of the same database transaction, but they clearly are not.
Please help me understand what I am doing wrong.
Just a thought as I was writing this edit. Could it be a problem that the method in the previous implementation class is annotated as @Transactional and the method that actually makes the JdbcTemplate.update() calls is also annotated as @Transactional? Could this nested declaration of @Transactional cause a problem?