sql-server 第二次更新在第一次更新之前执行

qyzbxkaa  于 2022-10-31  发布在  其他
关注(0)|答案(1)|浏览(152)

我在一个微服务架构中使用Spring Data JPA。我在MyService中有一个简单的保存方法(实际上它使用泛型,但是为了简单起见,让我们假设这是等价的),它位于两个应用程序之间的共享库中:

@SneakyThrows
@Transactional
public DTO save(DTO dto) {
    MyEntity entity = Optional
        .ofNullable(dto.getId())
        .flatMap(repository::findById)
        .orElse(new MyEntity());
    modelMapper.map(dto, entity);
    return modelMapper.map(repository.save(entity), DTO.class);
}

其中repository是一个JpaRepository。到目前为止,一切都很顺利。我有两个Spring应用程序;应用程序A执行一些操作,保存实体,然后调用应用程序B,方式如下:

DTO dto = myService.findById(id);
//modifying the dto...
myService.save(dto);
myFeignClient.callApplicationB(id);

应用程序B背后的结构类似(查找、编辑、保存),但在数据库上记录跟踪(我使用SQL Server,2016或2019的行为相同),我看到第二次更新(应用程序B的更新)在“第一次”更新(应用程序A的更新)之前执行。我是否遗漏了明显的配置,或者我应该在其他地方搜索该问题?

brgchamk

brgchamk1#

问题是以下@交易陷阱:使用保存方法的服务用@Transactional注解,因此所有方法都是事务性的,如下面的代码片段所示

@Transactional
void myMethod(Long id) {
    DTO dto = myService.findById(id);
    //modifying the dto...
    myService.save(dto);
    myFeignClient.callApplicationB(id);
}

我用下面的方法修改了代码

void myMethod(Long id) {
    myTransactionalMethod(id);
    myFeignClient.callApplicationB(id);
}

@Transactional
void myTransactionalMethod(Long id) {
    DTO dto = myService.findById(id);
    //modifying the dto...
    myService.save(dto);
}

另一种解决方案是,如果希望保持相同的事务边界,则将整个数据传递给应用程序B,并将其修改后返回:

@Transactional
void myMethod(Long id) {
    DTO dto = myService.findById(id);
    //modifying the dto...
    dto = myFeignClient.callApplicationB(dto);
    myService.save(dto);
}

编辑:我将试着澄清交易背后的逻辑。旧的逻辑如下:

  • 应用程序A打开(物理)事务A1并编辑dto;
    • 应用A打开事务A1内的(逻辑)事务A2,并保存dto;
  • 应用程序A关闭事务A2:由于A1事务仍处于打开状态,因此未将编辑提交到db;
  • 应用程序A调用应用程序B;
  • 应用程序B打开(物理)事务B1并编辑dto;
  • 应用程序B打开事务B1内的(逻辑)事务B2,并保存dto;
  • 应用程序B关闭事务B2;
  • 应用程序B回复应用程序A并关闭事务B1:B所做的更改被提交给db;
  • 应用程序A接收响应并关闭事务A1:A所做的更改将覆盖B所做的更改。

在解决方案中,事务A1在调用应用程序B之前关闭,因此A所做的更改在B所做的任何更改之前提交。

相关问题