【问题标题】:Hibernate retrieve null for OneToMany collectionHibernate 为 OneToMany 集合检索 null
【发布时间】:2016-07-11 02:06:58
【问题描述】:

我的问题是,当在以下对象上获取实例时,hibernate 在 @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
     */
}

这是“拥有”关联的对象:

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 数据库。 这是 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>

  • 测试,其中 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 的文档进行了几个小时的研究后,我仍然没有找到任何与相同问题/行为相关的帖子,所以我有点迷茫,非常感谢您的帮助!

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

编辑:

测试突出问题:

@Test
@Transactional
public void testFindUserAccount_OrganizationMemberCollectionFetching() {

    assertNotNull(userAccountDao.findUserAccount("user1@test.fr"));  //NoProblem
    assertNotNull(userAccountDao.findUserAccount("user1@test.fr").getCabinetMemberCollection());  //Fails

}

跟下面findUserAccountdao

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

【问题讨论】:

  • 您得到一个空值,因为您没有任何与您的 userAccount 关联的组织成员集合。你能提供你的测试代码吗?也许你在保存 OrganizationMember 之前忘记调用 setUserAccount 方法
  • 感谢您的快速回答,我认为在这种情况下,Hibernate 会返回我的 UserAccount 为空 Set,而不是 null 值,但也许我错了。我很确定 OrganizationMember 引用了用户帐户,因为数据库在测试之前就已填充。当我获取 OrganizationMember 时,hibernate 返回的对象具有对 UserAccount 的引用。
  • 您确定在测试中调用 getOrganizationMemberCollection 时持久性上下文没有关闭吗?如果我们能看到代码也许会有所帮助
  • 是的,我正在使用 Spring 来处理事务。我编辑帖子以包含显示问题的测试,谢谢!

标签: java hibernate one-to-many hibernate-onetomany


【解决方案1】:

问题是数据库填充和测试在同一个事务中运行,并且在这两个步骤之间没有清理休眠缓存。

结果是 hibernate 并没有真正触发对数据库的请求,而是访问了缓存并返回了对象,而没有与映射关系进行任何连接。

可能的解决方案是:

  • 在不同的事务中填充数据库。
  • 在填充后清理会话SessionFactory.getCurrentSession().clean();。 (如果需要,请在之前刷新会话:SessionFactory.getCurrentSession().flush();)。

每种可能性都会强制下一个查询真正命中数据库,因此会发生连接,映射的 Collection 将包含所需的数据(如果连接没有结果,则为空,但无论如何 Collection 不会有null 值)。

在我看来,第一个解决方案要好得多,因为如果测试中出现问题,它不会回滚整个人群。

【讨论】:

    【解决方案2】:

    这是一个延迟加载的集合,所以hibernate不会做任何初始化它的事情,hibernate在这里返回null是很正常的..

    我通常做的是在属性上声明一个空的HashSet

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "userAccount")
    private Set <OrganizationMember> organizationMemberCollection = new hashSet<>();
    

    【讨论】:

    • 感谢您的回答,我相信 FetchType.LAZY 意味着休眠将使用我的集合上的代理初始化我的集合,而不是空指针。
    • 实际上没有...= new hashSet&lt;&gt;(); 告诉休眠使用代理进行初始化...而不是 FetchType.LAZY
    • 尝试它解决了NPE但获得的Set是空的,看起来hibernate无法识别这个映射。我仍然不明白为什么它在真正的数据库上工作,而不是在内存数据库上。
    • 我在您的映射中没有发现任何问题...正如您所说,它在真实数据库上运行良好...您确定您的测试数据库有一些组织成员给定用户帐户(“user1@test.fr”)?
    • 是的,如果我运行organizationMemberDao.loadOrganizationMembersForUserAccount("user1@test.fr") 之类的东西,我会得到一个包含预期OrganizationMembers 的List&lt;OrganizationMember&gt;
    【解决方案3】:

    在我的情况下,这是因为我有“瞬态”,因为实体不可序列化,Sonar 告诉我添加瞬态关键字

    @OneToMany(mappedBy = "message", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
        private transient List<UserReadSystemMessage> usersRead;
    

    注意瞬态

    【讨论】:

      猜你喜欢
      • 2017-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-25
      • 2014-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多