Hibernate检索OneToMany集合返回null

5
我的问题是,当从以下对象检索实例时,Hibernate在@OneToMany Set organizationMemberCollection的值中检索到nullUserAccount.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
     */
}

以下是“拥有”该关联的对象:

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
     */
}

在这个应用程序中,我们有两个不同的配置:

  • 生产环境,其中Hibernate连接到PostgreSQL数据库。 以下是生产环境的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>

  • 测试环境,其中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(不是一个空的Set)。

经过几个小时的通过Google和Hibernate文档的研究后,我仍然没有找到任何与相同问题/行为有关的帖子,所以我有点迷失了,需要帮助!

如果需要,我当然可以提供更多信息,谢谢!

编辑:

突出显示问题的测试:

@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;
    }
}

你得到了一个 null,因为你的 userAccount 没有与 organizationMemberCollection 相关联。你能提供一下你测试的代码吗?也许你在保存 OrganizationMember 之前忘记调用 setUserAccount 方法了。 - Olivier Boissé
感谢您的快速回答,我认为在这种情况下,Hibernate会以空集合的形式返回我的UserAccount,而不是null值,但也许我错了。 我非常确定OrganizationMember引用了用户帐户,因为在测试之前数据库已经填充了。 当我获取OrganizationMember时,Hibernate返回的对象具有对UserAccount的引用。 - Tom
你确定在测试中调用getOrganizationMemberCollection时,持久化上下文没有关闭吗?如果我们能看到代码,也许可以帮助解决问题。 - Olivier Boissé
是的,我正在使用Spring来处理事务。我编辑了帖子并包含了一个显示问题的测试,谢谢! - Tom
3个回答

9
问题在于数据库填充和测试运行在同一事务中,hibernate缓存在这两个步骤之间没有被清除。结果是hibernate实际上没有向数据库发出请求,而是直接访问了缓存,并返回了对象,而没有与映射关系进行任何连接。
可能的解决方法包括:
- 在不同的事务中填充数据库。 - 在填充后清理Session SessionFactory.getCurrentSession().clean();(如果需要,请先刷新session:SessionFactory.getCurrentSession().flush();)。
每个可能性都会强制下一个查询真正地访问数据库,因此连接将发生,并且映射的集合将包含所需的数据(或为空,如果连接没有结果,但在任何情况下,该集合将不具有null值)。
在我看来,第一个解决方案更好,因为如果测试出现问题,它不会回滚整个填充过程。

2
这是一个懒加载集合,因此Hibernate不会执行任何初始化操作,这里返回null很正常..

我通常做的是在属性上声明一个空的HashSet
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount")
private Set <OrganizationMember> organizationMemberCollection = new hashSet<>();

谢谢你的回答,我相信FetchType.LAZY意味着Hibernate将使用代理来初始化我的集合,而不是空指针。 - Tom
其实不是这样的... = new hashSet <>(); 这个语句告诉 Hibernate 使用代理来初始化,而不是使用 FetchType.LAZY。 - Pras
尝试后,NPE问题已解决,但获得的Set为空,看起来Hibernate无法识别此映射。 我仍然不明白为什么它在真实数据库上运行正常,但在内存数据库上却不行。 - Tom
我在你的映射中没有看到任何问题...正如你所说,它在真实数据库上运行良好...你确定你的测试数据库有给定用户账户("user1@test.fr")的一些组织成员吗? - Pras
是的,如果我运行类似于 organizationMemberDao.loadOrganizationMembersForUserAccount("user1@test.fr") 的代码,我会得到一个包含预期的 OrganizationMemberList<OrganizationMember> - Tom

0
在我的情况下,是因为我将“瞬态”作为实体,而该实体无法序列化,Sonar 告诉我要添加 transient 关键字。
@OneToMany(mappedBy = "message", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
    private transient List<UserReadSystemMessage> usersRead;

注意瞬态


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接