spring-data-jpa 如何在测试中触发乐观锁定失败异常?

xwbd5t1u  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(166)

当我在2个不同的事务中保存对象时,代码中出现以下错误:
对象乐观锁定失败异常:具有标识符[]得类[]得对象:乐观锁定失败;嵌套的异常是组织。休眠。行已被另一事务更新或删除(或未保存值Map不正确):[]
我一直在尝试为异常编写测试。但是,所有测试都已通过。
我的测试:

@Slf4j
@Rollback
@Transactional
@DataJpaTest
@ActiveProfiles({"test", "h2"})
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(value = {
        StatusService.class
})
class StatusServiceTest {

    @Autowired
    private RequestRepository repository;
    @Autowired
    private StatusService statusService;

    private Request request;
    private final UUID requestUID = UUID.randomUUID();

    @BeforeEach
    void setUp() {
        request = new Request();
        request.setRequestUID(requestUID);
        repository.save(request);
    }

    @Test
    void updateStatus() {
        Request request1 = repository.findByRequestUID(requestUID);
        Request request2 = repository.findByRequestUID(requestUID);
        statusService.updateStatus(request1, "Done");
        statusService.updateStatus(request2, "In_progress");
    }
}

服务项目:

@Service
@Slf4j
@RequiredArgsConstructor
public class StatusService {
    private final RequestRepository repository;

    @Transactional
    public void updateStatus(Request request, String status) {
        // Some simple logic
        request.setStatus(status);
        repository.save(request); // The line with error
    }
}

产品型号:

@Entity
@Getter
@Setter
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Request {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonIgnore
    protected Long id;

    protected UUID requestUID;

    private String status;

    // Some additional fields

    @JsonIgnore
    @Version
    private Integer version;

}

我哪里错了?如何编写触发错误的测试?

kx5bkwkv

kx5bkwkv1#

@DataJpaTest包含@Transactional,因此您的测试会在单一交易中执行。
但是对于单个事务,JPA的一级缓存可以确保对于给定的类和id,您将始终获得相同的示例。这意味着request1request2实际上是相同的示例,并且只发生一次刷新,这将不会创建乐观锁定异常。
有两种方法可以创建乐观锁定异常。
1.实际上使用两个或更多事务。TransactionTemplate对此非常有用。您可以将request1的加载放在第一个事务中,然后加载、修改、保存和刷新request2,然后修改、保存和刷新request1。如果您想让它更接近生产环境中发生的情况,您可以启动两个线程来执行这些操作,并使用CountDownLatch确保它们继续执行以期望的顺序。
1.或者,您应该能够通过加载请求、修改其版本属性并刷新事务处理来触发所需的异常。当然,这与生产中发生的情况相差甚远,但正如所述,它应该会触发异常。

相关问题