更新 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