6

我们遇到了 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 的事务包装器......

4

6 回答 6

2

当您使用操纵它们实际设置的值的设置器时,休眠可能会发生非自愿更新。例如,String 属性的 setter 将 null 值替换为 ""。一个可能的候选者也是收藏品。确保 setter 不会替换包含的集合。如果你用另一个包含相同内容的集合替换一个实体的集合,hibernate 将无法意识到这一点并更新整个集合。

于 2009-03-31T18:14:00.273 回答
1

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

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

使用 readOnly 事务导致问题消失!

<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>

于 2009-04-23T15:40:31.073 回答
0

您是否检查过数据库中的任何触发器?您确定它是 Hibernate 而不是其他更新这些相同行的进程吗?也许有一列存储了最后一次读取的时间戳,并且每次读取该行时都会更新它(尽管我不记得你可以制作 SELECT 触发器)......

于 2009-03-19T22:08:39.573 回答
0

詹斯是对的

要添加-您需要仔细检查您的setter和getter,看看它们是否在不同的调用中返回不同的值,例如new Date()-这将返回一个新值-每次调用它都会让hibernate认为对象已经改变

于 2009-04-14T16:19:41.007 回答
0

有趣的一件事是添加

log4j.logger.org.hibernate.persister.entity.AbstractEntityPersister=TRACE

在您的 log4j 配置中。通过这样做,休眠将记录为什么实体需要在数据库中更新。当属性为空导致数据库更新时,我们遇到了一些实体返回“”的问题。通过这样做,我们能够查明此类问题。

于 2010-07-24T14:22:09.933 回答
0

当我们缺少索引时,我已经看到我们的系统中发生了这个问题。由于缺少键列上的索引,因此在数据库中执行的查询运行时间过长,从而锁定了表。

于 2010-07-24T18:21:19.050 回答