@Transactional 与 Spring 数据 returns null 的双向关系

@Transactional in bidirectional relation with Spring Data returns null

我正在使用 Spring 数据和@Transactional 注释(用于测试后自动回滚)。 我在帐户和用户(拥有方)之间有简单的双向关系:

@Entity
@Table(name = "ACCOUNT_T")
public class AccountEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String email;
    private String password;
    private String verificationCode;
    private Boolean active = false;

    @OneToOne(mappedBy = "account", fetch = FetchType.EAGER, 
              cascade = {CascadeType.MERGE, CascadeType.PERSIST,
                         CascadeType.DETACH, CascadeType.REFRESH})
    private UserEntity user;
}

@Entity
@Table(name = "USER_T")
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String surname;
    private String phone;
    private LocalDate birthDate;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
    @JoinColumn(name = "account_id")
    private AccountEntity account;
}

我正在使用 JpaRepositories 并将获取设置为 eager。 为什么有时当我从数据库中获取 objects 时我无法获取他们的 child objects-null 返回。这取决于我从哪一边添加 objects。 我使用 Junit5 编写了简单的测试:

@ExtendWith(SpringExtension.class)
@SpringBootTest
@Transactional
class UserAndAccountRepositoriesTest {

    void testA() {
        UserEntity userEntity = new UserEntity();
        setUserProperties(userEntity);
        AccountEntity accountEntity = new AccountEntity();
        setAccountProperties(accountEntity); //just setting values for fields
        accountEntity.setUser(userEntity);
        accountRepository.save(accountEntity);

        accountRepository.findAll().get(0).getUser(); //returns user
        userRepository.findAll().get(0).getAccount(); //returns null,but should return account related to that user
    }

    void testB() {
        UserEntity userEntity = new UserEntity();
        setUserProperties(userEntity);
        AccountEntity accountEntity = new AccountEntity();
        setAccountProperties(accountEntity);
        accountEntity.setUser(userEntity);
        accountRepository.save(accountEntity);

        accountRepository.findAll().get(0).getUser(); //again returns null,but shouldn't
        userRepository.findAll().get(0).getAccount(); //returns account
    }
}

没有 @Transactional 一切正常 - 我没有得到 null。 我做错了什么?

您需要设置关系的两端才能明确定义它。 尝试在您的设置案例中添加 userEntity.setAccount(accountEntity),这将解决问题。

Hibernate 不会帮助您并假设仅仅因为您设置了 a -> b,它会在另一个实体中为您设置 b <- a

它可能在没有 @Transactional 的情况下工作的原因是,如果没有注释,您会将设置数据提交到您正在使用的任何数据源中,并且最后不会回滚任何内容,并且由于您正在选择数据没有任何 idfindAll,你会得到以前的 user/account 个已经提交的实体,一些有关系,一些没有,因此你得到随机错误。

这是因为您没有在userEntity中设置帐号。请尝试如下:

userEntity.setAccount(accountEntity);

我将根据您是否在交易中解释行为不同的原因:

当您在交易时:

a) 任何获取您在此交易之前创建的实体 A(因此它已经在数据库中)将 return 一个 新对象 内存地址,hibernate 将解析它的双向关系,即使你没有明确设置它。

b) 任何获取您在此事务中先前创建的实体 B(因此尚未在 DB 中)的任何获取都将 return 中的相同对象 内存地址的术语,所以它实际上是同一个对象,因此如果你没有明确设置它的双向关系,它不会存在直到你设置它或直到事务结束(因为它会有效地将 B 保存在数据库中)并且你再次获取 B。

当您不在交易中时:

获取任何实体的任何行为都会像案例 a 中描述的那样)。

结论:

作者是情况b).