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

Posted

技术标签:

【中文标题】当我所做的只是一个列表时,为啥 Hibernate 会删除我的集合条目?【英文标题】:Why is Hibernate deleting my collection entries when all I do is a list?当我所做的只是一个列表时,为什么 Hibernate 会删除我的集合条目? 【发布时间】:2013-03-05 22:46:42 【问题描述】:

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

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

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

@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 表中的记录在这个页面被渲染后会被删除。

在为org.hibernate 启用DEBUG 并启用查询日志记录后,我在日志中发现了以下内容:

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>

我在这里遗漏了什么明显的东西吗?

【问题讨论】:

你从哪里得到“enable_lazy_load_no_trans”?我能找到的只是人们说它有问题,不推荐使用。 来自***.com/questions/578433/… “谨慎使用是轻描淡写!” “这实际上只是一个以基准测试为目标的功能......” “......这在一般用途中是一个非常糟糕的主意。” “使用风险自负。” “......它不是我们真正认为人们应该在不知道可能后果的情况下使用的功能。” hibernate.onjira.com/browse/HHH-7457 嗯,你感觉到那里的主题了吗?不要使用它。 @RyanStewart 你认为在这里使用OpenEntityManagerInViewFilter 会有什么好处吗? @RyanStewart 你能在这篇文章中添加一些建设性的东西吗?您的最后一条评论可以减少为“如果您在休眠中遇到错误,请使用 eclipselink”。 OEMINV/OSIV 没有比这更好的了,同样的特性在 eclipselink 中已经存在很长时间了。 【参考方案1】:

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

我建议的第一件事是删除这个初始化(它似乎是唯一删除角色的地方)虽然这本身是一个好主意(见下面的 cmets)我认为可能会干扰hibernate.enable_lazy_load_no_trans=true过去被leak connections 认识:

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

第二次尝试是检查如果您还使用 mappedBy 注释了 ManyToMany 关系的反面,是否以及发生了什么变化。

第三次尝试是,如果急切地加载集合可以解决问题(甚至更严重地启用 enable_lazy_load_no_trans 位置),使用 FetchType、Hibernate.initialize() 任何你想要的。

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

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

因此,使用 4.1.7 进行拍摄可能会产生更好的结果(或者可能更糟)。

* where may 的意思是:“您的案例非常相似,您被邀请将您的代码作为测试案例发送到错误报告中”。

【讨论】:

坏主意。 总是 将集合类型初始化为空集合。我保证这不是根本原因。 @Ryan Stewart 我同意,我的想法可能是 hibernate.enable_lazy_load_no_trans=true 的问题,我正在尝试这样做。 刚刚看到错误报告。我有完全相同的问题。我会尝试发送一个测试用例。降级到 4.1.7 似乎可以解决问题。但是,没有初始化集合并没有什么不同。甚至有一个 OEMIVFilter 工作。 这个 bug 还在 4.3.4 中。对我来说,它不会发生在急切地获取的收藏品上。降级到 4.1.7 也无济于事,因为此版本中还有其他错误阻止我使用它:/

以上是关于当我所做的只是一个列表时,为啥 Hibernate 会删除我的集合条目?的主要内容,如果未能解决你的问题,请参考以下文章

当实体未更改时,Hibernate 在刷新期间保持实体

我正在尝试打印出所有最长的字符串,而我所做的只是打印前 2 个字符串 [重复]

Android listview 不再突出显示单击时的选择

为啥这个新创建的 MVC 应用程序会抛出这个错误?

为啥 CORS 似乎不适用于 POST?

插入后选择Hibernate