Hibernate 为 OneToMany 集合检索 null
Hibernate retrieve null for OneToMany collection
我的问题是休眠在 @OneToMany Set organizationMemberCollection
的值中检索 null 在以下对象上获取实例时:
UserAccount.java :
@Entity
@Table(name="USER_ACCOUNT")
public class UserAccount {
@Id
@Column(name = "id", nullable = false)
@SequenceGenerator(name = "generator", sequenceName = "USER_ACCOUNT_id_seq", allocationSize=1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
private Long id;
@Column(name = "EMAIL", nullable = false)
private String email;
@Column(name = "PASSWORD_HASH")
private String passwordHash;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount")
private Set <OrganizationMember> organizationMemberCollection;
...
/*
* getters and setters
*/
}
这是 "owns" 关联的对象 :
OrganizationMember.java :
@Entity
@Table(name="ORGANIZATION_MEMBER")
public class OrganizationMember{
@Id
@Column(name = "id", nullable = false)
@SequenceGenerator(name = "generator", sequenceName = "ORGANIZATION_MEMBER_id_seq", allocationSize=1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "USER_ACCOUNT_ID", nullable = false)
private UserAccount userAccount;
...
/*
* getters and setters
*/
}
在此应用程序中,我们有两种不同的配置:
Production,其中 Hibernate 连接到 PostgreSQL 数据库。
这是 prod 的 sessionFactory 配置:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="hibernateProperties">
<props>
<prop key="hibernate.jdbc.batch_size">10</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cglib.use_reflection_optimizer">false</prop>
</props>
</property>
...
</bean>
Test,其中 Hibernate 连接到内存中的 HSQLDB 数据库:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.cglib.use_reflection_optimizer">false</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
</props>
</property>
...
</bean>
这个问题只出现在测试配置中;在生产配置中,一切进展顺利,我可以获得集合。
但是,当我在测试配置中获取 UserAccount 时,我在 organizationMemberCollection
属性(不是空集)中得到 null
。
通过 google 和 Hibernate 的文档进行了几个小时的研究后,我仍然没有找到任何与相同 issue/behaviour 相关的 post,所以我有点迷路了帮助将不胜感激!
如果需要,我当然可以提供更多信息,谢谢!
编辑:
测试突出问题:
@Test
@Transactional
public void testFindUserAccount_OrganizationMemberCollectionFetching() {
assertNotNull(userAccountDao.findUserAccount("user1@test.fr")); //NoProblem
assertNotNull(userAccountDao.findUserAccount("user1@test.fr").getCabinetMemberCollection()); //Fails
}
用下面的findUserAccount
dao
public UserAccount findUserAccount(String email) {
if (email == null) {
return null;
}
UserAccount userAccount = (UserAccount) this.sessionFactory
.getCurrentSession().createCriteria(UserAccount.class)
.add(Restrictions.eq("email", email).ignoreCase())
.uniqueResult();
if (userAccount == null) {
throw new ObjectNotFoundException("UserAccount.notFound");
} else {
return userAccount;
}
}
它是一个延迟加载的集合,所以 hibernate 不会对它进行任何初始化,很正常,hibernate returns null
这里..
我通常做的是在 属性 上声明一个空 HashSet
:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount")
private Set <OrganizationMember> organizationMemberCollection = new hashSet<>();
问题是数据库填充和测试 运行 在同一个事务中,并且在这两个步骤之间没有清理休眠缓存。
结果是 hibernate 并没有真正触发对数据库的请求,而是命中缓存并返回对象,而没有对映射关系进行任何连接。
可能的解决方案是:
- 在不同的事务中填充数据库。
- 填充后清除 Session
SessionFactory.getCurrentSession().clean();
。 (如果需要,请先刷新会话:SessionFactory.getCurrentSession().flush();
)。
每种可能性都会迫使下一个查询真正命中数据库,因此会发生连接并且映射的集合将包含想要的数据(或者如果连接没有结果则为空,但无论如何集合都会赢没有 null
值)。
在我看来,第一个解决方案要好得多,因为如果测试出现问题,它不会回滚整个群体。
在我的例子中,这是因为我有“transient”,因为实体不可序列化,Sonar 告诉我添加 transient 关键字
@OneToMany(mappedBy = "message", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
private transient List<UserReadSystemMessage> usersRead;
注意瞬变
我的问题是休眠在 @OneToMany Set organizationMemberCollection
的值中检索 null 在以下对象上获取实例时:
UserAccount.java :
@Entity
@Table(name="USER_ACCOUNT")
public class UserAccount {
@Id
@Column(name = "id", nullable = false)
@SequenceGenerator(name = "generator", sequenceName = "USER_ACCOUNT_id_seq", allocationSize=1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
private Long id;
@Column(name = "EMAIL", nullable = false)
private String email;
@Column(name = "PASSWORD_HASH")
private String passwordHash;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount")
private Set <OrganizationMember> organizationMemberCollection;
...
/*
* getters and setters
*/
}
这是 "owns" 关联的对象 :
OrganizationMember.java :
@Entity
@Table(name="ORGANIZATION_MEMBER")
public class OrganizationMember{
@Id
@Column(name = "id", nullable = false)
@SequenceGenerator(name = "generator", sequenceName = "ORGANIZATION_MEMBER_id_seq", allocationSize=1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "USER_ACCOUNT_ID", nullable = false)
private UserAccount userAccount;
...
/*
* getters and setters
*/
}
在此应用程序中,我们有两种不同的配置:
Production,其中 Hibernate 连接到 PostgreSQL 数据库。 这是 prod 的 sessionFactory 配置:
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="hibernateProperties"> <props> <prop key="hibernate.jdbc.batch_size">10</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.cglib.use_reflection_optimizer">false</prop> </props> </property> ... </bean>
Test,其中 Hibernate 连接到内存中的 HSQLDB 数据库:
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.cglib.use_reflection_optimizer">false</prop> <prop key="hibernate.hbm2ddl.auto">create-drop</prop> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.cache.use_query_cache">false</prop> </props> </property> ... </bean>
这个问题只出现在测试配置中;在生产配置中,一切进展顺利,我可以获得集合。
但是,当我在测试配置中获取 UserAccount 时,我在 organizationMemberCollection
属性(不是空集)中得到 null
。
通过 google 和 Hibernate 的文档进行了几个小时的研究后,我仍然没有找到任何与相同 issue/behaviour 相关的 post,所以我有点迷路了帮助将不胜感激!
如果需要,我当然可以提供更多信息,谢谢!
编辑:
测试突出问题:
@Test
@Transactional
public void testFindUserAccount_OrganizationMemberCollectionFetching() {
assertNotNull(userAccountDao.findUserAccount("user1@test.fr")); //NoProblem
assertNotNull(userAccountDao.findUserAccount("user1@test.fr").getCabinetMemberCollection()); //Fails
}
用下面的findUserAccount
dao
public UserAccount findUserAccount(String email) {
if (email == null) {
return null;
}
UserAccount userAccount = (UserAccount) this.sessionFactory
.getCurrentSession().createCriteria(UserAccount.class)
.add(Restrictions.eq("email", email).ignoreCase())
.uniqueResult();
if (userAccount == null) {
throw new ObjectNotFoundException("UserAccount.notFound");
} else {
return userAccount;
}
}
它是一个延迟加载的集合,所以 hibernate 不会对它进行任何初始化,很正常,hibernate returns null
这里..
我通常做的是在 属性 上声明一个空 HashSet
:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount")
private Set <OrganizationMember> organizationMemberCollection = new hashSet<>();
问题是数据库填充和测试 运行 在同一个事务中,并且在这两个步骤之间没有清理休眠缓存。
结果是 hibernate 并没有真正触发对数据库的请求,而是命中缓存并返回对象,而没有对映射关系进行任何连接。
可能的解决方案是:
- 在不同的事务中填充数据库。
- 填充后清除 Session
SessionFactory.getCurrentSession().clean();
。 (如果需要,请先刷新会话:SessionFactory.getCurrentSession().flush();
)。
每种可能性都会迫使下一个查询真正命中数据库,因此会发生连接并且映射的集合将包含想要的数据(或者如果连接没有结果则为空,但无论如何集合都会赢没有 null
值)。
在我看来,第一个解决方案要好得多,因为如果测试出现问题,它不会回滚整个群体。
在我的例子中,这是因为我有“transient”,因为实体不可序列化,Sonar 告诉我添加 transient 关键字
@OneToMany(mappedBy = "message", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
private transient List<UserReadSystemMessage> usersRead;
注意瞬变