我们遇到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);
}
}
调用此方法的服务的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>
通过调用LoadOrder加载的一个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>
更新:DataBase团队已在服务器上看到跟踪消息,这些消息似乎表明我们的“只读”进程实际上是自动写入数据库。记录的“UPDATE”命令是在我们从数据库中读取的确切列上执行的。似乎Hibernate会自动将这些记录写回数据库(即使我们没有要求它)。这可能解释了为什么会出现僵局。
这可能是因为会话FLUSH或类似的事情?看起来更像解决方案可能是使用带有readOnly的事务包装器......
答案 0 :(得分:2)
当您使用操作实际设置的值的setter时,hibernate可能会发生非自愿更新。一个例子是String属性的setter,它用null替换null值。可能的候选人也是收藏品。确保setter不替换包含的集合。如果用另一个包含相同内容的集合替换实体的集合,hibernate将无法实现并更新完整集合。
答案 1 :(得分:1)
我们最终确定解决方案是将其包装在readOnly事务中。
我不清楚为什么,我们根本没有使用setter(只是读取数据) - 数据库中没有任何变化。但由于某种原因,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>
答案 2 :(得分:0)
您是否检查过数据库中的任何触发器?你确定它是Hibernate而不是其他一些进程更新那些相同的行吗?也许有一个列存储了最后一次读取的时间戳,并且每次读取该行时都会更新(虽然我不记得我的头顶,你可以创建SELECT触发器)......
答案 3 :(得分:0)
Jens是对的
要添加 - 您需要仔细检查您的setter和getter,看看它们是否在不同的调用中返回不同的值 例如new Date() - 这会返回一个新值 - 每次调用它时都会让hibernate认为对象已经改变了
答案 4 :(得分:0)
答案 5 :(得分:0)