6

我似乎有一个相当奇怪的问题。我在 JSP 中显示用户及其角色。出于某种原因,角色仅在页面第一次加载时显示。我刷新页面,角色都从数据库中删除了!

我的设置是标准 Spring MVC、JPA + Hibernate (over Spring Data) 应用程序。(春季 3.2.x,休眠 4.1.8)

我有两个实体 -如下所示(假设 setter 和 getter UserRole

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

我有一个 Spring Data 存储库,它没有定义任何额外的方法。

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

我有一个服务和一个控制器,其相关方法如下。

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

在我的 JSP 中,我试图显示所有用户和任何关联的角色。

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

就像我之前说的,user_role在这个页面被渲染后,我的表中的记录被删除了。

在启用DEBUGorg.hibernate启用查询日志记录后,这是我在日志中发现的内容:

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]

很明显,这里发生了一些可疑的事情。为什么我的收藏首先被取消引用?

这是我的 JPA 实体管理器工厂定义。

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

我在这里遗漏了一些明显的东西吗?

4

1 回答 1

5

由于您发布的代码似乎正确,我在这里猜测。

我建议的第一件事是删除这个初始化(它似乎是唯一删除角色的地方),虽然这本身是一个好主意(见下面的评论)我认为可能会干扰hibernate.enable_lazy_load_no_trans=true已知泄漏连接过去:

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

于 2013-03-06T02:58:19.913 回答