@Transactional 如何处理测试方法?
How does @Transactional work on test methods?
我想知道@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 时,只会创建用户和角色条目。 table
MEMBER_ROLE
已创建但为空(无条目):未保存 n:n
@ManyToMany 单向关系。
- 当使用
@Commit
时,一切正常:有 2 个条目对应于 John Doe 用户的角色。
这是我的问题:
CrudRepository.save(...) 如何与@Transactional 一起工作,为什么单独使用此注释会产生 "partial save"?我们应该期望在没有 @Commit
的情况下不会向数据库提交任何内容(并且所有内容都已回滚)。
在测试方法上使用 @Transactional
是一个好习惯吗?
How does CrudRepository.save(...) work under the hood with @Transactional and why using this annotation alone does a "partial save"? We should expect that without @Commit nothing is committed to the database (and everything is roll backed).
由于是测试,由于 TransactionalTestExecutionListener
,交易应该默认回滚。现在你问为什么无论如何都要提交一些东西,这可能是因为内部交易。 @Transactional
跨越整个测试方法的事务,因此如果您使用某些 dao(如您的情况)该事务也将回滚,但是如果某些方法使用 REQUIRED
以外的事务传播类型,例如REQUIRED_NEW
,无论如何都可以执行对 db 的调用,因为 REQUIRED_NEW
从测试中暂停当前事务并创建一个新事务。那个新的将被承诺。还有刷新时间问题,由于hibernate缓存——他在事务结束时调用db,通常是在方法结束时,所以在持久化之后直接获取元素可能会失败。这两件事都可能很棘手。
Is it a good practice to use @Transactional on test methods?
当然可以,但不是为了测试事务边界。请记住,您还可以使用 @DataJpaTest
,它为每个测试方法放置 @Transactional
。
来源:
我想知道@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 时,只会创建用户和角色条目。 table
MEMBER_ROLE
已创建但为空(无条目):未保存n:n
@ManyToMany 单向关系。 - 当使用
@Commit
时,一切正常:有 2 个条目对应于 John Doe 用户的角色。
这是我的问题:
CrudRepository.save(...) 如何与@Transactional 一起工作,为什么单独使用此注释会产生 "partial save"?我们应该期望在没有 @Commit
的情况下不会向数据库提交任何内容(并且所有内容都已回滚)。
在测试方法上使用 @Transactional
是一个好习惯吗?
How does CrudRepository.save(...) work under the hood with @Transactional and why using this annotation alone does a "partial save"? We should expect that without @Commit nothing is committed to the database (and everything is roll backed).
由于是测试,由于 TransactionalTestExecutionListener
,交易应该默认回滚。现在你问为什么无论如何都要提交一些东西,这可能是因为内部交易。 @Transactional
跨越整个测试方法的事务,因此如果您使用某些 dao(如您的情况)该事务也将回滚,但是如果某些方法使用 REQUIRED
以外的事务传播类型,例如REQUIRED_NEW
,无论如何都可以执行对 db 的调用,因为 REQUIRED_NEW
从测试中暂停当前事务并创建一个新事务。那个新的将被承诺。还有刷新时间问题,由于hibernate缓存——他在事务结束时调用db,通常是在方法结束时,所以在持久化之后直接获取元素可能会失败。这两件事都可能很棘手。
Is it a good practice to use @Transactional on test methods?
当然可以,但不是为了测试事务边界。请记住,您还可以使用 @DataJpaTest
,它为每个测试方法放置 @Transactional
。
来源: