更新2个存储库的事务性服务方法



我想在我的项目中测试事务操作。基本上,如果抛出异常,我希望回滚userService.saveUser()操作。我已经简化了课程,你可以在下面找到它。

用户必须居住在某个地址中。一个地址可以有多个用户。

地址实体

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Address {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "STREET")
@NotNull
private String street;
@ToString.Exclude
@OneToMany(mappedBy = "address")
private Set<User> users = new HashSet<>();
}

用户实体

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "USER")
public class User {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "FIRSTNAME", nullable = false)
@NotNull
private String firstName;
@ManyToOne(fetch = FetchType.LAZY)
private Address address;
}

存储库

public interface AddressRepository extends CrudRepository<Address, Long> {
}
public interface UserRepository extends CrudRepository<User, Long> {
}

用户服务类

@Service
@Slf4j
@AllArgsConstructor
public class UserService {

@Autowired
AddressRepository addressRepository;
@Autowired
UserRepository userRepository;
@Transactional
public void saveUser(String firstName, String street) {
var address1 = Address.builder.street(street).build();
// to make sure that I have "id" of the address when I am saving it.
var addressSaved = addressRepository.save(address1);
if ("f1".equals(firstName))
throw new RuntimeException("some exception");
var user = User.builder()
.firstName(firstName)
.address(addressSaved)
.build();
// this operation can also throw DataIntegrityViolationException
userRepository.save(user);
}
}

这是我的测试班

@SpringBootTest
class UserServiceIT {
@Autowired
AddressRepository addressRepository;
@Autowired
UserRepository userRepository;
@Autowired
UserService userService;
@BeforeEach
void beforeEach() {
userRepository.deleteAll();
addressRepository.deleteAll();
}

@Test
void test_saveUser() {
assertThrows(RuntimeException.class,() -> userService.saveUser("f1", "s1"));
assertEquals(0, userRepository.count());
assertEquals(0, addressRepository.count());
}
@Test
void test_saveUser2() {
// column: nullable = false will throw the exception
assertThrows(DataIntegrityViolationException.class,() -> userService.saveUser(null, "s1"));
assertEquals(0, userRepository.count());
assertEquals(0, addressRepository.count());
}

}

这两个测试都在地址计数上给出断言错误(保存地址,不保存用户(。我希望对地址进行回滚(而不是保存(,因为保存地址后和保存用户时出现错误(违反了某些条件,因此必须回滚2次保存(。我做错了什么?

测试环境的application.yml

spring:
devtools:
restart:
enabled: false
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=false
driverClassName: org.h2.Driver
username: sa
password: 123
h2:
console:
enabled: false
jpa:
database-platform: org.hibernate.dialect.H2Dialect
database: H2
show-sql: false
hibernate:
ddl-auto: create

您可以通过以下链接访问整个示例项目:https://wetransfer.com/downloads/7cb870266e2e20f610b44d3cc9f229c220220308071438/7b88a2700076a3e53771e389c796cfe420220308071438/c777ab

您在这里发布的代码与您上传的原始代码中实际存在的代码不同。

原始代码:

@Transactional
void saveUser(String firstName, String street) {
var address = Address.builder().street(street).build();
var addressSaved = addressRepository.save(address);
if ("f1".equals(firstName))
throw new RuntimeException("f1");
var user = Person.builder()
.firstName(firstName)
.address(addressSaved)
.build();
personRepository.save(user);
}

这个方法实际上有默认的访问修饰符,所以CGLIB不能覆盖它并创建所需的逻辑。将该方法的访问修饰符更改为public

最新更新