@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
的情况下工作的原因是,如果没有注释,您会将设置数据提交到您正在使用的任何数据源中,并且最后不会回滚任何内容,并且由于您正在选择数据没有任何 id
和 findAll
,你会得到以前的 user/account 个已经提交的实体,一些有关系,一些没有,因此你得到随机错误。
这是因为您没有在userEntity
中设置帐号。请尝试如下:
userEntity.setAccount(accountEntity);
我将根据您是否在交易中解释行为不同的原因:
当您在交易时:
a) 任何获取您在此交易之前创建的实体 A(因此它已经在数据库中)将 return 一个 新对象 内存地址,hibernate 将解析它的双向关系,即使你没有明确设置它。
b) 任何获取您在此事务中先前创建的实体 B(因此尚未在 DB 中)的任何获取都将 return 中的相同对象 内存地址的术语,所以它实际上是同一个对象,因此如果你没有明确设置它的双向关系,它不会存在直到你设置它或直到事务结束(因为它会有效地将 B 保存在数据库中)并且你再次获取 B。
当您不在交易中时:
获取任何实体的任何行为都会像案例 a 中描述的那样)。
结论:
作者是情况b).
我正在使用 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
的情况下工作的原因是,如果没有注释,您会将设置数据提交到您正在使用的任何数据源中,并且最后不会回滚任何内容,并且由于您正在选择数据没有任何 id
和 findAll
,你会得到以前的 user/account 个已经提交的实体,一些有关系,一些没有,因此你得到随机错误。
这是因为您没有在userEntity
中设置帐号。请尝试如下:
userEntity.setAccount(accountEntity);
我将根据您是否在交易中解释行为不同的原因:
当您在交易时:
a) 任何获取您在此交易之前创建的实体 A(因此它已经在数据库中)将 return 一个 新对象 内存地址,hibernate 将解析它的双向关系,即使你没有明确设置它。
b) 任何获取您在此事务中先前创建的实体 B(因此尚未在 DB 中)的任何获取都将 return 中的相同对象 内存地址的术语,所以它实际上是同一个对象,因此如果你没有明确设置它的双向关系,它不会存在直到你设置它或直到事务结束(因为它会有效地将 B 保存在数据库中)并且你再次获取 B。
当您不在交易中时:
获取任何实体的任何行为都会像案例 a 中描述的那样)。
结论:
作者是情况b).