简体   繁体   English

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

[英]Multiple calls to JdbcTemplate.update not wrapped in a single DB transaction

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:我的代码创建了一个这样的 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 is an interface that defines a saveData method and gets implemented by DataFeedServiceImpl: 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 is another interface that gets implemented by DataFeedRepositoryImpl: DataFeedRepository 是另一个由 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().因此,在启动此操作的方法和对 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.)我的方法,在 class DataFeedRepositoryImpl 中,将事务数据保存到数据库表中,使用 @Transactional 进行注释,如下所示:(它还包括 @Override,因为它是来自基础 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.该方法为主事务表构建一个 INSERT 语句。 It needs to retrieve the new primary key, so it calls the update method of JdbcTemplate like this:它需要检索新的主键,所以它调用 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();

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:接下来,它为与该事务关联的图像构建一系列 INSERT 语句,并在对 JdbcTemplate.update() 的单独调用中将它们发送到数据库,如下所示:

    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.我希望所有这些对 update() 的调用都成为同一个数据库事务的一部分,但它们显然不是。

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?之前实现 class 中的方法被注释为@Transactional 并且实际进行 JdbcTemplate.update() 调用的方法也被注释为@Transactional,这可能是一个问题吗? Could this nested declaration of @Transactional cause a problem? @Transactional 的这种嵌套声明会导致问题吗?

This is a resolution but NOT an answer!这是一个解决方案,但不是答案!

The decision was may above my pay grade that this application will be re-architected and rewritten such that it uses the Java Persistence API (JPA) and not JdbcTemplates.该决定可能高于我的薪酬等级,即重新设计和重写此应用程序,使其使用 Java 持久性 API (JPA) 而不是 JdbcTemplates。

Will that have the same database transaction problems?那会有相同的数据库事务问题吗? We'll see.走着瞧。

This issue is closed.此问题已关闭。

Thank you to those who offered suggestions to this question.感谢那些为这个问题提供建议的人。 I would really like to fine the solution here, but that is not to be.我真的很想在这里完善解决方案,但事实并非如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM