@Transactional如何在测试方法中工作?

5

我想知道@Transactional在Spring Data JPA/Hibernate测试方法中是如何工作的。我在网上搜索了一些解释,但仍然不太清楚。

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

Member.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...

}

Role.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 被创建了但是为空(没有条目):即 @ManyToMany 单向关系未被保存。
  • 当使用 @Commit 时,一切正常:与 John Doe 用户角色对应的有两个条目。

以下是我的问题:

CrudRepository.save(...) 在使用 @Transactional 时的内部工作原理是什么?为什么只使用这个注解会导致“部分保存”?我们本应该期望在没有 @Commit 的情况下,不会提交任何内容到数据库中(而是全部回滚)。

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


1
我从未在测试方法中使用过事务,所有的时间都是在Service类中使用它。普通的测试类会初始化数据,执行测试,并回滚数据更改以提供测试数据集的原始副本,这确保了每个单独的测试用例运行后不会修改任何数据。这样可以确保下一个测试用例不会受到前一个测试用例的影响,并且测试将在每次运行时产生相同的结果。 - VinhNT
1个回答

9
在@ Transactional下,CrudRepository.save(...)如何在幕后工作,为什么单独使用此注释会进行“部分保存”?我们应该期望在没有@Commit的情况下不会将任何内容提交到数据库(并且所有内容都将被回滚)。
由于这是一个测试,事务应默认回滚感谢TransactionalTestExecutionListener。现在您正在问为什么仍然提交了某些内容,这可能是由于内部事务。@Transactional跨越整个测试方法的事务,因此如果您使用一些dao(例如在您的情况下),该事务也将回滚,但是如果某个方法使用事务传播类型而不是REQUIRED,例如REQUIRED_NEW,则可以执行对DB的调用,因为REQUIRED_NEW挂起测试当前事务并创建新事务。那个新的将被提交。还有刷时间问题,由于Hibernate缓存-他在事务结束时通常在方法结束时向DB发出调用,因此直接在持久化它们后获取元素可能会失败。这两件事可能会很棘手。
在测试方法上使用@Transactional是否是一个好习惯?
当然可以,但不是为了测试事务边界。请记住,您也可以使用 @DataJpaTest,它会为每个测试方法放置 @Transactional
来源:
  1. TransactionalTestExecutionListener
  2. Automatic Rollback of Transactions in Spring Tests

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接