6

I seem to have a rather strange problem. I display users and their roles in a JSP. For some reason, the roles are displayed only for the first time the page is loaded. I refresh the page and the roles are all deleted from the database!

My setup is standard Spring MVC, JPA + Hibernate (over Spring Data) application. (Spring 3.2.x, Hibernate 4.1.8)

I have two entities - User and Role as shown below (assume the setters and getters)

@Entity
public class User {
    @Id
    @GeneratedValue
    private int id;

    private String name;

    @ManyToMany
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();
}

@Entity
public class Role {
    @Id
    private String id;

    private String name;
}

I have a Spring Data repository which doesn't define any extra methods.

public interface UserRepository extends CrudRepository<User, Integer> { 
}

I have a service and a controller whose relevant methods are as follows.

// service
@Transactional(readOnly = true)
public Iterable<User> findAll() {
    return userRepository.findAll();
}

// controller
@RequestMapping
public String showUsers(ModelMap model) {
    model.put("users", userService.findAll());

    return "admin/users";
}

In my JSP, I am trying to display all the users and any associated roles.

<c:forEach var="user" items="${users}">
    <tr>
        <td>${user.name}</td>
        <td>
           <c:forEach var="role" items="${user.roles}">
                ${role.name}
            </c:forEach>
        </td>
    </tr>
</c:forEach>

Like I said earlier, the records in my user_role table get deleted after this page has been rendered.

Upon enabling DEBUG for org.hibernate and enabling query logging, here is what I found in the logs:

22:36:25 DEBUG Collection dereferenced: [com.adarshr.domain.User.roles#1] [Collections.java:76]
22:36:25 DEBUG Flushed: 0 insertions, 0 updates, 0 deletions to 2 objects [AbstractFlushingEventListener.java:117]
22:36:25 DEBUG Flushed: 0 (re)creations, 0 updates, 1 removals to 1 collections [AbstractFlushingEventListener.java:124]
22:36:25 DEBUG Deleting collection: [com.adarshr.domain.User.roles#1] [AbstractCollectionPersister.java:1124]
22:36:25 DEBUG 
    delete 
    from
        user_role 
    where
        user_id=? [SqlStatementLogger.java:104]
22:36:25 DEBUG Done deleting collection [AbstractCollectionPersister.java:1182]

Quite clearly something fishy is going on here. Why is my collection being dereferenced in the first place?

Here is my JPA entity manager factory definition.

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="mainDataSource" />
    <property name="packagesToScan" value="com.adarshr.domain" />
    <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <value>
            hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
            hibernate.format_sql=${hibernate.format.sql}
            hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
            hibernate.show_sql=${hibernate.show.sql}
            hibernate.enable_lazy_load_no_trans=true
        </value>
    </property>
</bean>

Am I missing something obvious here?

adarshr
  • 57,189
  • 21
  • 133
  • 158
  • Where did you get "enable_lazy_load_no_trans" from? All I can find about it is people saying it's buggy and not recommended for use. – Ryan Stewart Mar 06 '13 at 05:15
  • From http://stackoverflow.com/questions/578433/how-to-solve-lazy-initialization-exception-using-jpa-and-hibernate-as-provider – adarshr Mar 06 '13 at 07:22
  • "Use with caution is an understatement!" "This is really just a benchmark-targeted feature..." "...this is a really bad idea in general uses." "use at own risk." "...its not a feature we really think people should use without knowing the possible consequences." https://hibernate.onjira.com/browse/HHH-7457 Hmm, are you sensing a theme there? Don't use it. – Ryan Stewart Mar 06 '13 at 13:33
  • @RyanStewart Do you think using `OpenEntityManagerInViewFilter` would be any good here? – adarshr Mar 06 '13 at 14:26
  • @RyanStewart could you please add something constructive to this post? your last comment could be reduced as "if you hit a bug in hibernate, just use eclipselink". OEMINV/OSIV does nothing better than this, and the same feature has been present in eclipselink for long time. – guido Mar 06 '13 at 14:34
  • @adarshr you are definitely hitting the bug linked below, did you try with hibernate 4.1.7 yet? – guido Mar 06 '13 at 14:35
  • I tried even with 4.2CR2 yet no luck. However, strangely, when I used OEMIVF, the problem seemed to go away. Not sure what to do now. – adarshr Mar 06 '13 at 16:29
  • The bug report is still open in jira and without a test case probably nobody started to look at it yet; the only version reported to work is 4.1.7. If I were you I'd move away for now, but I really invite you to post your error there and eventually contribute a self-contained test case extracted from your project. – guido Mar 06 '13 at 22:34
  • @guido: In no way was I suggesting EclipseLink over Hibernate. My point was to not use a feature (enable_lazy_load_no_trans) that was never intended for production use, which the Hibernate maintainers *repeatedly* urge us not to use, and which seems to be what was causing the problem after all. I'm sorry that I wasn't clear enough. – Ryan Stewart Mar 07 '13 at 03:32
  • @adarshr: Yes, I'd highly recommend you use the OEMIVF instead, since that's the more-or-less conventional solution to this problem, but I see that you seem to have already come to that conclusion. – Ryan Stewart Mar 07 '13 at 03:33

1 Answers1

5

As the code you posted seems correct, I am guessing here.

The first thing I would suggest is to remove this initialization (it seems the only place where roles are removed) which while is a good idea by itself in general (see comments below) I think may interfere with hibernate.enable_lazy_load_no_trans=true which has been known to leak connections in the past:

 private Set<Role> roles = new HashSet<>();

A second try would be to check if and what changes if you also annotate the inverse side of your ManyToMany relation with mappedBy.

A third attempt would be if eagerly loading the collection fixes the problem (aggravating enable_lazy_load_no_trans position even more), using FetchType, Hibernate.initialize() of whatever you want.

Last one is getting rid of enable_lazy_load_no_trans and use OpenSessionInView

EDIT: ahh, last one, you may* have it this bug (hibernate 4.1.8 and 4.1.9 affected): https://hibernate.onjira.com/browse/HHH-7971

So having a shot with 4.1.7 could give better results (or maybe worse).

* where may is to be intended as: "your case is so similar you are invited to send your code as a test case in the bug report".

guido
  • 17,668
  • 4
  • 66
  • 89
  • Bad idea. *Always* initialize collection types to an empty collection. I guarantee this isn't the root cause. – Ryan Stewart Mar 06 '13 at 03:08
  • @Ryan Stewart I agree, my idea that could be a problem having hibernate.enable_lazy_load_no_trans=true and I was giving a shot at that. – guido Mar 06 '13 at 03:10
  • Just saw the bug report. I have exactly the same issue. I will try sending a test case. Downgrading to 4.1.7 seemed to fix the problem. However, not having the collection initialised did not make a difference. Even having an OEMIVFilter worked. – adarshr Mar 06 '13 at 22:38
  • This bug is still in 4.3.4. For me it does not happen on eagerly fetched collections. Downgrading to 4.1.7 does not help either as there are other bugs in this version which prevent me using it :/ – Vojtěch Mar 15 '14 at 09:12