多次调用JdbcTemplate.更新没有封装在单个DB事务中



我看了几个例子,不明白我在做什么不同;然而,这对我不起作用。以下是对我的代码的简化描述,其中包含代码片段,以说明哪些地方不起作用。

我有一个主事务表,为每个应用程序事务保存一条记录。我有一个链接表,其中包含来自事务的图像,外键指向主事务记录。这里没有太奇怪或花哨的东西。(充分披露:还有另外两个链接表,但我确信一个表的修复将适用于所有表。)

我的代码创建了一个RESTful端点,如下所示:
@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是一个接口,它定义了一个saveData方法,并由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是由DataFeedRepositoryImpl实现的另一个接口:

public class DataFeedRepositoryImpl implements DataFeedRepository {
private static final int REF_SS_MFA = 4;
@Resource(name="rdpJdbcTemplate")
private JdbcTemplate jdbcTemplate;

因此,在启动此操作的方法和对saveData()的实际调用之间至少有两层类。

我的方法,在类DataFeedRepositoryImpl中,将事务数据保存到数据库表中,用@Transactional注释,如下所示:(它还包括@Override,因为它是基类的接口实现。)

@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 {

该方法为主事务表构建一条INSERT语句。它需要检索新的主键,所以它像这样调用JdbcTemplate的更新方法:

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();

接下来,它为与此事务关联的图像构建一系列INSERT语句,并通过单独调用JdbcTemplate.update()将它们发送到数据库,如下所示:

for (DataFeedImage image : images) {
...
newRowCount = jdbcTemplate.update(thisquery);
}

我的期望是,在任何将图像插入链接的图像表的调用中的错误将导致整个数据库事务回滚。然而,我的观察是不同的:

  • 主事务数据记录仍然在主事务表中。
  • 所有在错误之前插入的图像仍然存在于图像表中。

我希望所有这些对update()的调用都是同一个数据库事务的一部分,但它们显然不是。

请告诉我我做错了什么。

只是我写这篇编辑时的一个想法。前一个实现类中的方法被注释为@Transactional,而实际使JdbcTemplate.update()调用的方法也被注释为@Transactional,这可能是一个问题吗?@Transactional的嵌套声明会导致问题吗?

这是一个分辨率但是NOT一个答案!

这个决定可能超出了我的工资级别,这个应用程序将被重新架构和重写,以便它使用Java持久化API (JPA)而不是JdbcTemplates。

会有相同的数据库事务问题吗?我们将会看到。

此问题已解决。

感谢那些对这个问题提出建议的人。我真的很想在这里找到解决办法,但这是不可能的。

最新更新