当我所做的只是一个列表时,为什么Hibernate会删除我的集合条目?

ada*_*shr 6 java spring hibernate jpa spring-data-jpa

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

我的设置是标准的Spring MVC,JPA + Hibernate(通过Spring Data)应用程序.(Spring 3.2.x,Hibernate 4.1.8)

我有两个实体- User并且Role如下图所示(假设getter和setter方法)

@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;
}
Run Code Online (Sandbox Code Playgroud)

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

public interface UserRepository extends CrudRepository<User, Integer> { 
}
Run Code Online (Sandbox Code Playgroud)

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

// 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";
}
Run Code Online (Sandbox Code Playgroud)

在我的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>
Run Code Online (Sandbox Code Playgroud)

就像我之前说过的那样,我的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]
Run Code Online (Sandbox Code Playgroud)

很明显这里有些可疑的东西.为什么我的收藏品首先被解除引用?

这是我的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>
Run Code Online (Sandbox Code Playgroud)

我错过了一些明显的东西吗?

gui*_*ido 5

由于你发布的代码似乎是正确的,我猜这里.

我建议的第一件事就是删除这个初始化(好像在那里角色被删除的唯一的地方),这虽然是一个好主意本身一般(见下文评论)我想可能干扰hibernate.enable_lazy_load_no_trans=true其已经知道泄漏的连接在过去:

 private Set<Role> roles = new HashSet<>();
Run Code Online (Sandbox Code Playgroud)

如果您还使用mappedBy注释ManyToMany关系的反面,则第二次尝试将检查是否以及更改.

第三次尝试是如果急切加载集合修复了问题(加剧了enable_lazy_load_no_trans位置甚至更多),使用FetchType,Hibernate.initialize()无论你想要什么.

最后一个是摆脱enable_lazy_load_no_trans并使用OpenSessionInView

编辑:啊,最后一个,你可能*有这个bug(hibernate 4.1.8和4.1.9受影响):https: //hibernate.onjira.com/browse/HHH-7971

因此,使用4.1.7可以获得更好的结果(或者可能更糟).

* 其中可能的目的是:"您的情况非常相似,您被邀请将您的代码作为测试用例发送到错误报告中".