@Transactional 与 Spring Data 的双向关系返回 null

Posted

技术标签:

【中文标题】@Transactional 与 Spring Data 的双向关系返回 null【英文标题】:@Transactional in bidirectional relation with Spring Data returns null 【发布时间】:2019-04-06 05:47:21 【问题描述】:

我正在使用 Spring Data 和 @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 并且获取设置为渴望。 为什么有时当我从数据库中获取对象时,我无法获取他们的孩子 返回对象-null。这取决于我从哪一侧添加对象。 我使用 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 一切正常 - 我不会为空。 我做错了什么?

【问题讨论】:

你能在保存之前尝试设置userEntity.setAccount(accountEntity)吗? 那行得通 :) 但是为什么呢?没有@Transactional,我不必从两边添加 通常你需要为任何类型的关系设置双方。 Hibernate 需要这种级别的明确性 为什么..因为它们相互关联并且默认情况下不连接,除非您为它们设置默认值,因此为空。如果您明确设置它们,正如@buræquete 所指出的那样,它们就是。 我明白,但没有@Transactional 我只能从一侧设置并且它可以工作。我知道我很好奇:) 【参考方案1】:

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

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

它在没有@Transactional 的情况下可能工作的原因是,如果没有注释,您将设置数据提交到您正在使用的任何数据源中,并且最后没有回滚,因为您选择的数据没有任何@987654325 @ 与findAll,您将获得先前已提交的用户/帐户实体,有些有关系,有些没有,因此您得到随机错误。

【讨论】:

【参考方案2】:

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

userEntity.setAccount(accountEntity);

【讨论】:

【参考方案3】:

我将根据您是否在交易中解释为什么行为会有所不同:

当您进行交易时:

a) 任何获取您在此事务之前创建的实体 A 的任何获取(因此它已经在数据库中)将在内存地址方面返回一个 新对象,并且休眠将解析其双向关系,即使您没有明确设置。

b) 任何获取您在此事务中较早创建的实体 B 的任何获取(因此尚未在 DB 中)将在内存地址方面返回 相同的对象,所以它确实是同一个对象,因此如果你没有明确设置它的双向关系,它不会存在,直到你设置它或直到事务结束(因为它会有效地将 B 保存在 DB 中)并且你再次获取 B。

当您不参与交易时:

获取任何实体的任何获取都将像情况 a) 中描述的那样。

结论:

作者是b)。

【讨论】:

以上是关于@Transactional 与 Spring Data 的双向关系返回 null的主要内容,如果未能解决你的问题,请参考以下文章

Spring @Transactional on 类与方法

Spring @transactional 是不是与 MongoDB 一起使用?

如何将 @Transactional 与 Spring Data 一起使用?

@Transactional 与 Spring Data 的双向关系返回 null

spring,mybatis事务管理配置与@Transactional注解使用

spring,mybatis事务管理配置与@Transactional注解使用