对 JdbcTemplate.update 的多次调用未包含在单个数据库事务中

问题描述

我看过几个例子,但不明白我在做什么不同;然而,这对我不起作用。这是我的代码的简化描述,包含代码片段,以说明什么不起作用。

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

我的代码创建了一个这样的 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 的嵌套声明会导致问题吗?

解决方法

这是一个解决方案,但不是答案!

这个应用程序将被重新架构和重写,使其使用 Java Persistence API (JPA) 而不是 JdbcTemplates,这个决定可能高于我的薪酬等级。

会不会有同样的数据库事务问题?我们拭目以待。

此问题已关闭。

感谢为这个问题提供建议的人。我真的很想完善这里的解决方案,但事实并非如此。