当Hibernate应用程序加载数据以供只读使用时,Oracle死锁

jon*_*anq 5 oracle spring deadlock hibernate

我们遇到Oracle死锁(org.hibernate.util.JDBCExceptionReporter - ORA-00060:在等待资源时检测到死锁)错误.有人建议问题在于使用Hibernate执行只读操作的进程,而另一个进程在同一行上执行更新.

有问题的只读进程是使用Hibernate和Spring配置的.我们没有明确定义服务的事务.虽然这可能不太理想 - 我不明白为什么Hibernate会在没有执行保存/更新操作时尝试在行上获得独占锁定 - 只有get/load.

所以我的问题是:当没有定义显式事务管理时,Hibernate是否尝试在一行上获得读/写锁,即使只执行了对象的"加载".不执行保存/更新.

是否有可能围绕正在加载数据的服务定义一个事务,然后在transactionAttributes上特别说READONLY会导致Hibernate忽略已经存在的行锁并只是加载数据以用于只读目的?

以下是一些代码示例:

为了加载记录,我们使用的是HibernateDaoTemplate

public class HibernatePurchaseOrderDataService extends HibernateDaoSupport implements PurchaseOrderDataService {
    public PurchaseOrderData retrieveById(Long id) {
        return (PurchaseOrderData)getHibernateTemplate().get(PurchaseOrderData.class, id);
    }
}
Run Code Online (Sandbox Code Playgroud)

调用此方法的服务的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>
Run Code Online (Sandbox Code Playgroud)

实际死锁发生在一个PurchaseOrderItem记录上,该记录是通过对LoadOrder的加载调用加载的.

如果正在加载的记录被另一个进程锁定,是否会导致死锁?如果是这样 - 添加一个事务包装器,如下面的那个解决问题?

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

更新:DataBase团队已在服务器上看到跟踪消息,这些消息似乎表明我们的"只读"进程实际上是自动写入数据库.记录的"UPDATE"命令是在我们从数据库中读取的确切列上执行的.似乎Hibernate会自动将这些记录写回数据库(即使我们没有要求它).这可能解释了为什么会出现僵局.

这可能是因为会话FLUSH或类似的东西?看起来更像解决方案可能是使用带有readOnly的事务包装器...

jon*_*anq 1

我们最终确定解决方案是将其包装在只读事务中。

我不清楚为什么,我们根本没有使用设置器(只是读取数据)——数据库中没有任何变化。但由于某种原因,Hibernate 试图重新写回相同的数据,并在另一个进程尝试读取这些记录时导致锁定。

使用只读事务使问题消失!

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