繁体   English   中英

测试期间的Spring Boot JPA事务-插入时不引发键冲突异常

[英]Spring Boot JPA Transaction during Test - not throwing key violation exception on insert

我有以下测试,该测试生成带有公司ID和电子邮件的邀请。 然后,我们使用相同的公司ID和电子邮件生成第二个。 这将违反数据库上的唯一约束(请参见邀请实体)。 事实是,测试通过了以下列表(我猜该交易永远不会提交第二次插入)

如果我在第二次插入之后执行findAll() (请参阅下面的注释),则在findAll()发生索引冲突的行上会出现异常。

当方法返回时,如何使UserService的方法UserService其事务,因此测试代码将按原样抛出异常?

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserServiceTest {
    ... Everything is autowired
    @Test
    public void testCreateInvitation() {
        String email = "test2@example.com";
        Company company = userService.createCompany("ACMETEST", tu);

        assertTrue(invitationRepository.count() == 0l);
        assertNotNull(userService.sendInvitation(company, email));
        assertTrue(invitationRepository.count() == 1l);

        assertTrue(invitationRepository.findAllByEmail(email).size() == 1);
        // Second should throw exception
        userService.sendInvitation(company, email);

        // If this line is uncommented, I get key violation exception, otherwise, test passes!!!!????
        //Iterable<Invitation> all = invitationRepository.findAll();
    }

邀请类别和约束:

@Entity
@Table(uniqueConstraints=@UniqueConstraint(columnNames={"email", "company_id"}))
@JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id")
@Data
public class Invitation {

    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    String id;

    @ManyToOne
    @JoinColumn(name="company_id")
    private Company company;

    String email;

    Date inviteDate;

    Date registerDate;
}

用户服务:

@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class UserService {
    public Invitation sendInvitation(Company company, String toEmail) {
        Invitation invitation = new Invitation();
        invitation.setCompany(company);
        invitation.setInviteDate(new Date());
        invitation.setEmail(toEmail);
        try {
            // TODO send email
            return invitationRepository.save(invitation);
        } catch (Exception e) {
            LOGGER.error("Cannot send invitation: {}", e.getMessage());
        }
        return null;
    }
}

当方法返回时,如何使UserService中的方法落实其事务,因此测试代码将按原样抛出异常?

由于@DataJpaTest@Transactional本身。 @Test方法编写的代码在事务内执行。 您可以使用Propogation.NOT_SUPPORTED更改它

非事务执行,如果存在当前事务,则挂起当前事务

@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testCreateInvitation() {
    // your test method body unchanged
}

使用此方法,您应该在第二个userService.sendInvitation(company, email);上获得异常userService.sendInvitation(company, email); 呼叫。

也有一些其他选择。

选项1:

@Commit添加到您的测试方法。

标有@Transactional所有测试将在执行后回滚 但是Hibernate有一个微妙的事情:它可能会延迟sql的执行,直到您提交事务(或显式调用flush (选项2))为止。

由于事务已回滚,因此没有执行插入操作,因此没有违反约束的情况。

要注意的是使用@Commit你会得到你的测试方法之外抛出的异常。

选项2:

使用手动flush()

如果您的存储库extends JpaRepository<>

@Test
public void testCreateInvitation() {
    // create first invitation,
    // create second invitation
    invitationRepository.flush();
}

否则注入EntityManager并在其上调用flush()

@PersistenceContext
private EntityManager em;

@Test
public void testCreateInvitation() {
    // create first invitation,
    // create second invitation
    em.flush();
}

当你将得到一个异常invitationRepository.flush()em.flush()调用。

选项3:使用GenerationType.IDENTITY:

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;

save()后,Hibernate必须为@Entity分配ID。 但是,如果不执行insert ,就无法获取实体的ID。

言:有一件事使我感到困惑。

您使用@Transactional(propagation = Propagation.REQUIRES_NEW) ,它应在执行该方法后强制提交事务。 请检查您的日志,以查看事务是否确实有效。

暂无
暂无

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

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