更新 2 个存储库的事务服务方法
Transactional Service Method that updates 2 repositories
我想在我的项目中测试事务操作。基本上,如果抛出异常,我想回滚 userService.saveUser()
操作。我已经简化了 classes,您可以在下面找到它。
用户必须住在一个地址。一个地址可以有多个用户。
地址实体
@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> {
}
用户服务Class
@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);
}
}
这是我的测试class
@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
您可以从此 link 获得整个示例项目: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
我想在我的项目中测试事务操作。基本上,如果抛出异常,我想回滚 userService.saveUser()
操作。我已经简化了 classes,您可以在下面找到它。
用户必须住在一个地址。一个地址可以有多个用户。
地址实体
@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> {
}
用户服务Class
@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);
}
}
这是我的测试class
@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
您可以从此 link 获得整个示例项目: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