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?
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".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With