我们遇到了 Oracle 死锁(org.hibernate.util.JDBCExceptionReporter - ORA-00060:在等待资源时检测到死锁)错误。有人建议问题出在使用 Hibernate 执行只读操作的进程,而另一个进程正在对同一行执行更新。
有问题的只读进程是使用 Hibernate 和 Spring 配置的。我们没有明确定义服务的事务。虽然这可能并不理想 - 我不明白为什么 Hibernate 会在没有执行保存/更新操作时尝试在一行上获得排他锁 - 只有一个获取/加载。
所以我的问题是:Hibernate 在没有定义明确的事务管理时是否尝试在行上获得读/写锁,即使只执行对象的“加载”。不执行保存/更新。
是否有可能围绕正在加载数据的服务定义一个事务,然后在 transactionAttributes 上明确说 READONLY 会导致 Hibernate 忽略已经存在的行锁并仅出于只读目的加载数据?
以下是一些代码示例:
为了加载记录,我们使用 HibernateDaoTemplate
public class HibernatePurchaseOrderDataService extends HibernateDaoSupport implements PurchaseOrderDataService {
public PurchaseOrderData retrieveById(Long id) {
return (PurchaseOrderData)getHibernateTemplate().get(PurchaseOrderData.class, id);
}
}
调用此方法的服务的 Spring 配置为:
<bean id="orderDataService"
class="com.example.orderdata.HibernatePurchaseOrderDataService">
<property name="sessionFactory" ref="orderDataSessionFactory"/>
</bean>
<bean id="orderDataSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="hibernateDataSource"/>
<property name="hibernateProperties" ref="hibernateProperties"/>
<property name="mappingResources">
<list>
<value>com/example/orderdata/PurchaseOrderData.hbm.xml</value>
<value>com/example/orderdata/PurchaseOrderItem.hbm.xml</value>
</list>
</property>
</bean>
实际死锁发生在通过调用加载 PurchaseOrder 加载的 PurchaseOrderItem 记录之一上。
如果正在加载的记录被另一个进程锁定,这会导致死锁吗?如果是这样 - 添加一个如下所示的事务包装器会解决问题吗?
<bean id="txWrappedOrderDataService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target" ref="orderDataService"/>
<property name="transactionAttributes">
<props>
<!-- all methods require a transaction -->
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
更新:数据库团队在服务器上看到了似乎表明我们的“只读”进程实际上正在自动写入数据库的跟踪消息。在我们从数据库中读取的确切列上执行了记录的“更新”命令。似乎 Hibernate 会自动将这些记录写回数据库(即使我们没有要求它这样做)。这可能可以解释为什么会出现死锁。
这可能是因为 Session FLUSH 或类似的原因吗?看起来更像是解决方案可能是使用带有 readOnly 的事务包装器......