这是一个设计问题,未提交具体代码以保护我的底线。
使用Hibernate时,标准工作流程如下:
可能迭代到2-4。
Session.clear()的合理用例是什么?
答:我遇到的具体问题是加载和修改实体的(大)代码,然后清除()会话,基本上抛弃了所做的更改。 (要完成的业务任务不包括修改实体,因此代码“有效”)。
对我来说,正确的设计是确保(大)代码不会进行不想保存的更改?
B:我猜想Session.clear()是为了方便/灵活而存在,不是因为使用它是个好主意。
我是否误解了Hibernate哲学?
C:Subquestion:框架代码在任务完成时无条件清除()会话是不是一个坏主意?恕我直言,如果任务完成时会话是脏的,框架应该抱怨!应该关闭会话,看看任务完成...(忽略一分钟的表现)
(标签A,B和C,以便您可以指出您要回答的部分)。
答案 0 :(得分:29)
广告。 :看起来您知道clear()
的作用。明确调用它的原因是从L1缓存中删除所有托管实体,以便在一个事务中处理大型数据集时它不会无限增长。
它会放弃对托管网站所做的所有更改未明确保留。这意味着您可以安全地修改实体,明确更新它并清除会话。这是正确的设计。显然,如果没有进行任何更改(长,但只读会话),clear()
始终是安全的。
您也可以使用stateless sessions。
广告。 B :不,它存在的原因如上:确保L1(会话缓存)不会增长太多。当然,手动维护它是一个糟糕的想法,并表明另一个工具应该用于大型数据集,但有时它是必须的。
请注意,在JPA规范中,还有clear()
和flush()
方法。在这种情况下,在调用flush()
之前,应始终先调用clear()
将更改推送到数据库(显式更新)。
广告。 C :当他/她用脏更改清除会话时,警告用户(可能通过发出警告消息而不是抛出异常)实际上是一个好主意。此外,我不认为框架代码应该无条件地调用clear()
,除非它确定它运行的用户代码刷新或不做任何更改。
答案 1 :(得分:3)
这是我刚遇到的另一个原因:在同一事务中多次调用存储过程时缓存以前的结果。简化代码如下。
//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(),第一次调用的resultset行被复制到第二次调用的resultset中。我正在使用Oracle 11gR2。
复制此错误的关键是在同一事务中进行两次调用。由于我在视图模式中使用开放会话,因此两个调用都自动发生在同一事务中(因为原始代码在存储每个结果的循环中调用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>