gpt4 book ai didi

java - Spring 存储库并不总是抛出 DataIntegrityViolationException

转载 作者:行者123 更新时间:2023-12-01 20:27:11 25 4
gpt4 key购买 nike

我正在使用 Spring 制作 REST API,但在对其进行单元测试时遇到了麻烦。我编写了一个端点来更新用户组,当我在前端创建一个具有重复名称的组时(unique=true),它确实会发送 409 冲突。然而,当我进行单元测试时却没有。我发现在单元测试末尾添加此行 groupRepository.findAll().forEach(g -> System.out.println(g.getName())); 确实会抛出 409 .

端点:

@Override
public ResponseEntity<Object> update(@ApiParam(value = "form object to add to the store", required = true) @Valid @RequestBody FormGroupDTO group, @ApiParam(value = "Id of the form that needs to be updated", required = true) @PathVariable("groupId") Long groupId, @ApiParam(value = "token to be passed as a header", required = true) @RequestHeader(value = "token", required = true) String token) {
String name = JWTutils.getEmailInToken(token);

if(name == null) {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}

User user = userRepository.findByEmail(name);

if(user == null){
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}

FormGroup groupModel = groupRepository.findByIdAndAdmin(groupId, user);

if(groupModel == null){
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}

if(group.getMembers().stream().filter(m -> m.getRole() == UserFormGroupRole.ADMIN).toArray().length == 0){
return new ResponseEntity<>(new ValidationErrorDTO("noAdmin", "MEMBER.NOADMIN"), HttpStatus.BAD_REQUEST);
}

// Get users
groupModel.getUserFormGroups().clear();
for(MemberDTO member : group.getMembers()){
User u = userRepository.findByEmail(member.getEmail());
if(u == null){
return new ResponseEntity<>(new ValidationErrorDTO("notexist", "ADDUSER.NOTEXIST"), HttpStatus.BAD_REQUEST);
}
if(u.getRole() == UserRole.USER){
return new ResponseEntity<>(new ValidationErrorDTO("notexist", "ADDUSER.NOTPRIVILEGED"), HttpStatus.BAD_REQUEST);
}
UserFormGroup ufg = userFormGroupRepository.findByUserAndFormGroup(u, groupModel);
if(ufg == null){
groupModel.getUserFormGroups().add(new UserFormGroup(u, groupModel, member.getRole()));
} else{
ufg.setRole(member.getRole());
groupModel.getUserFormGroups().add(ufg);
}
}

groupModel.setName(group.getName());

try{
groupRepository.save(groupModel);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (DataIntegrityViolationException e){
System.out.println(e.getMessage());
System.out.println(e.getClass());
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
}

我的单元测试:

@Test
public void updateGroupNameAlreadyInUse() throws Exception {
groupRepository.save(new FormGroup("newFormGroup", user));
this.mockMvc.perform(put("/groups/" + group.getId())
.header("token", token)
.content(json(new FormGroupDTO("newFormGroup", group.getUserFormGroups().stream().map(ufg -> new MemberDTO(ufg.getUser().getEmail(), ufg.getRole())).collect(Collectors.toList()))))
.contentType(contentType))
.andExpect(status().isConflict());
}

我的 CrudRepository 的保存函数并不总是抛出 DataIntegrityViolationException。我刚刚意识到,单元测试的第一行 groupRepository.save(new FormGroup("newFormGroup", user)); 可能不会在单元测试结束之前执行,并且 findAll 函数会触发它。

最佳答案

简短的故事:在测试中插入后,您需要手动执行flush()。现在我们来详细讨论一下。我假设您正在使用 ID 生成策略,例如序列或 UUID 或类似的策略。

有很多事情需要考虑:

冲洗

  • FlushMode - 确定 ORM 何时触发 SQL 语句。默认情况下,它在任何 SELECT 语句之前和事务提交之前触发。您的解决方案获取每条记录的名称会发出一个 SELECT 语句 - 该语句会触发刷新所有待处理的 SQL 语句。
  • save()persist() 保证返回持久对象。这样的对象必须有一个ID。某些 ID 生成策略(例如 Identity)需要使用 INSERT 语句来生成 ID。其他(例如 SequenceUUID)- 不需要。因此,ORM 可以在不插入记录的情况下获取 ID(为了进行一些优化,它希望尽可能延迟插入记录)。

因此,在进行与 ORM 相关的测试时,您必须在使用该数据执行任何操作之前手动调用 flush()

交易和 session

当您将事物标记为@Transactional时,行为是:

  1. 查看事务是否已在此线程中打开。
    • 如果是 - 不要做任何事情。
    • 如果没有 - 创建一个事务并将其绑定(bind)到当前线程(通过 ThreadLocal 变量)。
  2. 当方法完成时 - 检查事务是否是由我启动的。
    • 如果不是 - 什么都不做。
    • 如果是 - 提交事务,关闭 session 。

我假设您使用@Transactional标记您的测试。这意味着它是测试谁启动 session 和事务。存储库仅使用已打开的存储库。然后由于没有提交 - 没有刷新。然后您使用 MockMvc - 在同一线程中工作。它通过@Transactional 或OSIV 也发现事务已经开始。因此事务被重用。

然后进入您的核心逻辑 - 您正在执行一些 SELECT 语句,这会刷新当前 session 中待处理的 SQL 语句。所以你原来的 save() 只是刷新了。

现在,在逻辑的最后,您再次执行 save() ,它将 INSERT 语句放入待处理的 SQL 查询中。测试完成后,它只是回滚事务,并且最终的 INSERT 不会发生。除非..您正在执行您提到的 SELECT 语句。

最后 - 不要忘记在 ORM 相关测试中执行 flush()clear() 。这些方法存在于 Hibernate 的 Session 或 JPA 的 EntityManager 中。前者可以通过:SessionFactory#getCurrentSession()来完成。后者可以通过以下方式注入(inject):

@PersistenceContext 
EntityManager entityManager;

PS:我没有看到任何带有 @Transactional 标记的生产代码。如果不这样做,您可能会遇到问题。

PPS:this is not a unit test .

关于java - Spring 存储库并不总是抛出 DataIntegrityViolationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43707774/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com