org.hibernate.Session.clear() 被认为是有害的?

2022-09-01 15:56:47

这是一个设计问题,具体代码没有提交来保护我的屁股。

使用休眠时,标准工作流如下所示:

  1. 公开会议
  2. 开始交易
  3. 开展业务(读取和修改数据)
  4. 提交事务
  5. 关闭会话

可能迭代到2-4。

Session.clear() 的合理用例是什么?

答:我遇到的具体问题是一段(大)代码,它加载和修改实体,然后清除会话,基本上丢弃了所做的更改。(要完成的业务任务不包括修改实体,因此代码“有效”)。

在我看来,正确的设计是确保(大)代码段不会进行更改,它不想保存?

B:我猜Session.clear()的存在是为了方便/灵活,而不是因为使用它是个好主意。

我是否误解了冬眠哲学?

C:子问题:框架代码在任务完成时无条件清除会话是不是一个坏主意?恕我直言,如果任务完成时会话脏污,框架应该抱怨!会话应该关闭,因为任务已完成...(忽略性能)

(标签 A、B 和 C,以便您可以指示要回答的部分)。


答案 1

广告。答:看起来您知道该怎么做。显式调用它的原因是要从 L1 缓存中删除所有托管实体,以便在一个事务中处理大型数据集时,它不会无限增长。clear()

它将丢弃对未显式保留的托管实体所做的所有更改。这意味着您可以安全地修改实体、显式更新实体并清除会话。这是正确的设计。显然,如果未进行任何更改(长但只读会话),则始终是安全的。clear()

您还可以使用无状态会话

Ad. B:不,它的存在是出于上述原因:确保L1(会话缓存)不会增长太多。当然,手动维护它是一个糟糕的主意,并且表明应该将另一个工具用于大型数据集,但有时它是必须的。

请注意,在JPA规范中也有和方法。在这种情况下,在调用 之前,应始终先调用以将更改推送到数据库中(显式更新)。clear()flush()flush()clear()

Ad. C:当用户清除带有脏更改的会话时,警告用户(可能是通过发出警告消息而不是抛出异常)实际上是一个好主意。另外,我不认为框架代码应该无条件调用,除非它确定它运行的用户代码刷新或不进行任何更改。clear()


答案 2

以下是我刚刚遇到的另一个原因:在同一事务中多次调用存储过程时缓存以前的结果。简化代码,如下所示。

//Begin transaction
SessionFactory sf = HibernateSessionFactory.getFactory();
Session dbSession = sf.getCurrentSession();
dbSession.beginTransaction();

//First call to stored procedure
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA");
query.setString("custName", "A");
List<ShipSummaryRow> shipSummaryRows = query.list();

//Second call to stored procedure
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA");
query.setString("custName", "B");
List<ShipSummaryRow> shipSummaryRows = query.list();

//Commit both    
dbSession.getTransaction().commit();

在第一次调用后没有 clear(),则第一次调用的结果集行将复制到第二次调用的结果集中。我使用的是Oracle 11gR2。

复制此 Bug 的关键是在同一事务中进行两次调用。由于我使用的是视图模式中的开放会话,因此两个调用都自动发生在同一事务中(因为原始代码在存储每个调用结果的循环中调用proc)。因此,我称之为错误;else可以被认为是一个功能,但即使这样,clear()也没有在代码示例中被调用,说明它应该被调用。session.flush() 什么也没做。映射文件,如下所示。因此,我已将 clear() 添加到所有过程调用的末尾。尚未使用我的自定义 SQL 调用进行测试。这是微不足道的事情;惊讶于错误的存在。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.jfx.rr.model.ShipSummaryRow">
        <id name="id" type="integer"/>
        <property name="shipQtrString" not-null="true" type="string"/>
        <property name="shipAmount" not-null="true" type="double"/>
    </class>
    <sql-query callable="true" name="RR_CUST_OPP_DATA">
        <return class="com.jfx.rr.model.ShipSummaryRow">
            <return-property column="SHIPPED_ID" name="id"/>
            <return-property column="SHIP_QTR" name="shipQtrString"/>
            <return-property column="SHIPPED_AMOUNT" name="shipAmount"/>
        </return>
        { call RR_DASHBOARD_REPORTS_PKG.RR_CUST_OPP_DATA(?, :custName) }
    </sql-query>
</hibernate-mapping>

推荐