【问题标题】:Multiple calls to JdbcTemplate.update not wrapped in a single DB transaction对 JdbcTemplate.update 的多次调用未包含在单个数据库事务中
【发布时间】:2021-05-14 13:53:39
【问题描述】:

我看了几个例子,不明白我在做什么不同;但是,这对我不起作用。这是我的代码的简化描述,带有代码 sn-ps,以说明什么不起作用。

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

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

【问题讨论】:

  • 您不是偶然使用 MyISAM 表吗(因为它们不支持事务,而您使用的是 MySQL,这将是最简单的解释)?否则,行为应该是您所期望的行为,并且需要完整的代码和更多的调试信息。
  • 所有表都是使用 InnoDB 存储引擎创建的。
  • 你在哪里打电话给saveData?不同的组件,而不是来自同一类中的方法?如果从同一个类调用,它会阻止@Transactional 属性生效导致这种行为。
  • @Kayaman 是的,闻起来完全像一个自我呼叫。
  • 我真的希望你是对的,但我看不出来。

标签: java mysql spring transactions jdbctemplate


【解决方案1】:

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

这个决定可能超出了我的薪酬等级,该应用程序将被重新架构和重写,以便它使用 Java Persistence API (JPA) 而不是 JdbcTemplates。

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

此问题已关闭。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多