简体   繁体   English

Hibernate 为 OneToMany 集合检索 null

[英]Hibernate retrieve null for OneToMany collection

My problem is that hibernate retrieve null in the value of the @OneToMany Set organizationMemberCollection when fetching an instance on the following object :我的问题是,在获取以下对象上的实例时,hibernate 在@OneToMany Set organizationMemberCollection的值中检索null

UserAccount.java :用户帐户.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
     */
}

Here is the Object that "owns" the association :这是“拥有”关联的对象:

OrganizationMember.java :组织成员.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
     */
}

In this application we have two different configuations :在这个应用程序中,我们有两种不同的配置:

  • Production , where Hibernate is connected to a PostgreSQL database. Production ,其中 Hibernate 连接到 PostgreSQL 数据库。 Here is the sessionFactory configuration for prod :这是 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 , where Hibernate is conencted to an in memory HSQLDB database :测试,其中 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>

This issue only show up in testing configuration;此问题仅出现在测试配置中; In production configuration everything's going nicely and I can get the collection.在生产配置中,一切进展顺利,我可以获得集合。 However, when I fetch an UserAccount in the test configuration I get null in the organizationMemberCollection property (Not an empty Set).但是,当我在测试配置中获取 UserAccount 时,我在organizationMemberCollection属性(不是空集)中得到null

After some hours of research through google and Hibernate's doc I still haven't found any post relating to the same issue/behaviour, so I'm a lillte bit lost and help would be greatly appreciated !通过谷歌和 Hibernate 的文档进行了几个小时的研究后,我仍然没有找到任何与相同问题/行为相关的帖子,所以我有点迷失了,将不胜感激!

I can of course provide more information if needed, Thanks !如果需要,我当然可以提供更多信息,谢谢!

Edit :编辑:

Test higlighting the problem :测试突出问题:

@Test
@Transactional
public void testFindUserAccount_OrganizationMemberCollectionFetching() {

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

}

With the following findUserAccount dao使用以下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;
    }
}

The issue was that the database population and the test were running in the same transaction, and hibernate cache wasn't cleaned between these two steps.问题是数据库填充和测试在同一个事务中运行,并且在这两个步骤之间没有清除休眠缓存。

The consequence was that hibernate didn't really fired the request to the database, but hit the cache instead and returned the object without doing any join with the mapped relation.结果是 hibernate 并没有真正向数据库发出请求,而是命中缓存并返回对象,而没有与映射关系进行任何连接。

The possible solutions are :可能的解决方案是:

  • Populate the database in a different Transaction.在不同的事务中填充数据库。
  • Clean the Session SessionFactory.getCurrentSession().clean();清理会话SessionFactory.getCurrentSession().clean(); after the population.人口之后。 (Flush the session before if needed : SessionFactory.getCurrentSession().flush(); ). (如果需要,在之前刷新会话: SessionFactory.getCurrentSession().flush(); )。

Each possibility will force the next query to really hit the database, therefore the join will occur and the mapped Collection will contain the wanted data (or be empty if the join has no result, but in any case the Collection won't have the null value).每种可能性都会迫使下一个查询真正命中数据库,因此会发生连接并且映射的集合将包含所需的数据(如果连接没有结果,则为空,但在任何情况下,集合都不会具有null值)。

In my opinin the first solution is way better as it doesn't rollback the whole population if something goes wrong in the test.在我看来,第一个解决方案要好得多,因为如果测试出现问题,它不会回滚整个人口。

It's a lazy loaded collection, so hibernate doesn't do anything to initialize it, quite normal that hibernate returns null here ..这是一个延迟加载的集合,所以 hibernate 不做任何初始化它的事情,很正常的 hibernate 在这里返回null ..

What I usually do is declare an empty HashSet on the property :我通常做的是在属性上声明一个空的HashSet

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

In my case it was because I had "transient" as the entity was not serializable an Sonar told me to add the transient keyword就我而言,这是因为我有“瞬态”,因为实体不可序列化,声纳告诉我添加瞬态关键字

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

Note the transient注意瞬态

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM