Hibernate retrieve null for OneToMany collection

Tom picture Tom · Mar 23, 2016 · Viewed 7.3k times · Source

My problem is that hibernate retrieve null in the value of the @OneToMany Set organizationMemberCollection when fetching an instance on the following object :

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

Here is the Object that "owns" the association :

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

In this application we have two different configuations :

  • Production, where Hibernate is connected to a PostgreSQL database. Here is the sessionFactory configuration for prod :

    <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 :

    <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).

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 !

I can of course provide more information if needed, Thanks !

Edit :

Test higlighting the problem :

@Test
@Transactional
public void testFindUserAccount_OrganizationMemberCollectionFetching() {

    assertNotNull(userAccountDao.findUserAccount("[email protected]"));  //NoProblem
    assertNotNull(userAccountDao.findUserAccount("[email protected]").getCabinetMemberCollection());  //Fails

}

With the following 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;
    }
}

Answer

Tom picture Tom · Apr 8, 2016

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.

The possible solutions are :

  • Populate the database in a different Transaction.
  • Clean the Session SessionFactory.getCurrentSession().clean(); after the population. (Flush the session before if needed : 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).

In my opinin the first solution is way better as it doesn't rollback the whole population if something goes wrong in the test.