【问题标题】:How does @Transactional work on test methods?@Transactional 如何处理测试方法?
【发布时间】:2020-06-02 17:27:12
【问题描述】:

我想知道 @Transactional 如何在测试方法上与 Spring Data JPA / Hibernate 一起使用。我在网上搜索了一些解释,但似乎仍然晦涩难懂。

下面是我正在使用的代码:

会员.java

@Entity 
@Table(name = "MEMBER")
public class Member  {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    String firstname;

    String lastname;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
          name = "MEMBER_ROLE",
          joinColumns = @JoinColumn(name = "member_id"),
          inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<Role>();

    // Getters...
    // Setters...

}

角色.java

@Entity
@Table(name = "ROLE")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    private String description;

    public Role(String name, String description) {

        this.name = name;
        this.description = description;
    }

    @ManyToMany(mappedBy = "roles", cascade = CascadeType.ALL)
    private Set<User> users = new HashSet<User>();

    public void addUser(User user) {
        users.add(user);
        user.getRoles().add(this);
    }

    // Getters...
    // Setters...

}

MemberRepositoryTest.java

@SpringBootTest
public class MemberRepositoryTest {

    @Autowired
    private MemberRepository memberRepository;


    @Test
    @Transactional
    //@Commit // When @Commit is uncommented, the relation is saved in the database
    public void testSave() {

        Member testMember = new Member();

        testMember.setFirstname("John");
        testMember.setLastname("Doe");

        Role role1 = new Role("ROLE_USER", "The role of for basic users");
        Role role2 = new Role("ROLE_ADMIN", "The role of for admin users");

        testMember.getRoles().add(role1);
        testMember.getRoles().add(role2);

        this.memberRepository.save(testMember);

        // The 2 roles are well in the set
        System.out.println("********" + this.memberRepository.findById(1).get().getRoles().toString());

    }

}

  • 问题是,当我单独使用@Transactional 而没有@Commit 时,只会创建用户和角色条目。表 MEMBER_ROLE 已创建但为空(无条目):n:n @ManyToMany 单向关系未保存。
  • 使用 @Commit 时,一切正常:有 2 个条目对应于 John Doe 用户的角色。

这是我的问题:

CrudRepository.save(...) 如何使用 @Transactional 在幕后工作,为什么单独使用这个注解会“部分保存”?我们应该预期,如果没有@Commit,则不会向数据库提交任何内容(并且所有内容都会回滚)。

在测试方法上使用@Transactional 是一种好习惯吗?

【问题讨论】:

  • 我从来没有在测试方法上使用过Transaction,一直以来,我在Service类Test类中以正常方式使用它来初始化数据,执行测试,并回滚数据更改给原始测试数据集,确保在每个测试用例运行后都不会修改数据。使下一个测试用例不受前一个测试用例的影响,并且每次测试都会以相同的结果运行。

标签: hibernate jpa junit transactions jta


【解决方案1】:

CrudRepository.save(...) 如何在后台使用 @Transactional 工作,为什么单独使用此注释会“部分保存”?我们应该预料到,如果没有 @Commit,则不会向数据库提交任何内容(并且所有内容都会回滚)。

由于这是一个测试,由于TransactionalTestExecutionListener,事务应该默认回滚。现在您要问为什么要提交某些内容,这可能是由于内部事务。 @Transactional 跨越整个测试方法的事务,因此,如果您使用某些 dao(如您的情况)该事务也将回滚,但是如果某些方法使用 REQUIRED 以外的事务传播类型,例如 REQUIRED_NEW,无论如何都可以执行对 db 的调用,因为REQUIRED_NEW 会暂停测试中的当前事务并创建一个新事务。那个新的将被承诺。由于休眠缓存,还存在刷新时间问题 - 他在事务结束时调用 db,通常在方法结束时,因此在持久化元素后直接获取元素可能会失败。这两件事都可能很棘手。

在测试方法上使用@Transactional 是一种好习惯吗?

当然,但不是为了测试事务边界。请记住,您也可以使用@DataJpaTest,它将@Transactional 用于每个测试方法。

来源:

  1. TransactionalTestExecutionListener
  2. Automatic Rollback of Transactions in Spring Tests

【讨论】:

    猜你喜欢
    • 2020-02-14
    • 1970-01-01
    • 2019-02-26
    • 1970-01-01
    • 2023-03-07
    • 2023-03-22
    • 2019-03-30
    • 2019-08-06
    相关资源
    最近更新 更多