当 Hibernate 应用程序加载数据以供只读使用时 Oracle 死锁
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/663250/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Oracle Deadlock when Hibernate application loading data for readonly use
提问by jonathanq
We are experiencing an Oracle Deadlock (org.hibernate.util.JDBCExceptionReporter - ORA-00060: deadlock detected while waiting for resource) error. It has been suggested that the issue is with a process that is performing readonly operations using Hibernate while another process is performing an update on the same row.
我们遇到了 Oracle 死锁(org.hibernate.util.JDBCExceptionReporter - ORA-00060:在等待资源时检测到死锁)错误。有人建议问题出在使用 Hibernate 执行只读操作的进程,而另一个进程正在对同一行执行更新。
The readonly process in question is configured using Hibernate and Spring. We have not explicitly defined a transaction for the service. While that may not be ideal - I fail to see why Hibernate would try to get an exclusive lock on a row when no save/update operations were performed - only a get/load.
有问题的只读进程是使用 Hibernate 和 Spring 配置的。我们没有明确定义服务的事务。虽然这可能并不理想 - 我不明白为什么 Hibernate 会在没有执行保存/更新操作的情况下尝试获取行上的排他锁 - 只有获取/加载。
So my question is: Does Hibernate, when no explicit transaction management is defined, try to get a read/write lock on a row even if only a "load" of an object is performed. No Save/Update is performed.
所以我的问题是:当没有定义显式事务管理时,Hibernate 是否会尝试在行上获得读/写锁,即使只执行了对象的“加载”。不执行保存/更新。
Is it possible that defining a transaction around the service that is loading data, and then specifically saying READONLY on the transactionAttributes would cause Hibernate to ignore an already existing row lock and just load the data for readonly purposes?
是否有可能围绕正在加载数据的服务定义一个事务,然后在 transactionAttributes 上特别说 READONLY 会导致 Hibernate 忽略已经存在的行锁,而只是出于只读目的加载数据?
Here are some code examples:
下面是一些代码示例:
For loading the record we are using a HibernateDaoTemplate
为了加载记录,我们使用了 HibernateDaoTemplate
public class HibernatePurchaseOrderDataService extends HibernateDaoSupport implements PurchaseOrderDataService {
public PurchaseOrderData retrieveById(Long id) {
return (PurchaseOrderData)getHibernateTemplate().get(PurchaseOrderData.class, id);
}
}
The Spring configuration for the service calling this method is:
调用此方法的服务的 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>
The actual deadlock is occurring on one of the PurchaseOrderItem records being loaded by the call to the load the PurchaseOrder.
实际死锁发生在通过调用加载 PurchaseOrder 加载的 PurchaseOrderItem 记录之一上。
Would this cause a deadlock if the record being loaded was locked by another process? And if so - would adding a transaction wrapper such as the one below solve the problem?
如果正在加载的记录被另一个进程锁定,这会导致死锁吗?如果是这样 - 添加一个如下所示的交易包装器会解决问题吗?
<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>
Update: The DataBase team has seen trace messages on the server that seem to indicate that our "readonly" process is actually writing to the database automatically. There are logged "UPDATE" commands that are performed on the exact columns we are reading from the database. It seems that Hibernate is automatically writing these records back out to the database (even though we aren't asking it to). That would probably explain why there is a deadlock.
更新:DataBase 团队在服务器上看到了似乎表明我们的“只读”进程实际上正在自动写入数据库的跟踪消息。记录了对我们从数据库中读取的确切列执行的“更新”命令。似乎 Hibernate 会自动将这些记录写回数据库(即使我们没有要求它)。这可能可以解释为什么会出现僵局。
Could this be because of a Session FLUSH or something similar? Looking more like the solution might be to use a transaction wrapper with readOnly on it...
这可能是因为会话 FLUSH 或类似的原因吗?看起来更像是解决方案可能是使用带有只读的事务包装器......
采纳答案by jonathanq
We finally determined that the solution was to wrap it in a readOnly transaction.
我们最终确定解决方案是将其包装在只读事务中。
I am not clear why, we were not using the setters at all (simply reading the data) - nothing was getting changed in the database. But for some reason Hibernate was trying to re-write the same data back and was causing a lock when another process tried to read those records.
我不清楚为什么,我们根本没有使用 setter(只是读取数据) - 数据库中没有任何变化。但出于某种原因,Hibernate 试图重新写回相同的数据,并在另一个进程试图读取这些记录时导致锁定。
Using the readOnly transaction caused the problem to go away!
使用只读事务导致问题消失!
<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>
回答by Jens Schauder
Involuntary updates might happen with hibernate when you use setters that manipulate the value they actually set. An example would be a setter for an String attribute that replaces a value of null with "". A likely candidate are also collections. Make sure setters don't replace the contained collection. If you replace a collection of an entity with a another collection containing the same contents, hibernate will not be able to realize that and update the full collection.
当您使用操作它们实际设置的值的 setter 时,hibernate 可能会发生非自愿更新。例如,String 属性的 setter 将 null 值替换为 ""。一个可能的候选者也是集合。确保 setter 不会替换包含的集合。如果你用另一个包含相同内容的集合替换一个实体的集合,hibernate 将无法意识到这一点并更新完整的集合。
回答by Lionel Touati
One thing interesting to do is to add
一件有趣的事情是添加
log4j.logger.org.hibernate.persister.entity.AbstractEntityPersister=TRACE
log4j.logger.org.hibernate.persister.entity.AbstractEntityPersister=TRACE
in your log4j configuration. By doing that hibernate will log why an entity needs update in the database. We had some issues with entities returning "" when a property was null causing updates in the DB. By doing that we were able to pinpoint such problems.
在您的 log4j 配置中。通过这样做,休眠将记录为什么实体需要在数据库中更新。当属性为空导致数据库更新时,我们遇到了实体返回“”的一些问题。通过这样做,我们能够查明这些问题。
回答by Sasi
I have seen this issue happen in our system when we had missing indexes. The queries that are being executed in the database are running too long due to missing indexes on key columns thus locking up the table.
当我们缺少索引时,我已经看到这个问题发生在我们的系统中。由于缺少键列上的索引,数据库中正在执行的查询运行时间过长,从而锁定了表。
回答by RN.
Jens is right
詹斯是对的
To add on- you need to closely inspect your setters and getters and see if they return a different value on different calls e.g new Date ()- this would return a new value- each time its called and will make hibernate think the object has changed
添加 - 您需要仔细检查您的 setter 和 getter 并查看它们是否在不同的调用中返回不同的值,例如 new Date () - 这将返回一个新值 - 每次调用时都会使 hibernate 认为对象已更改
回答by Chochos
Have you checked for any triggers in the database? Are you sure it's Hibernate and not some other process updating those same rows? Maybe there is a column storing a timestamp of the last read and it gets updated every time the row is read (Although I can't remember off the top of my head that you could make SELECT triggers)...
您是否检查过数据库中的任何触发器?您确定它是 Hibernate 而不是其他更新这些相同行的进程吗?也许有一列存储了上次读取的时间戳,并且每次读取该行时都会更新它(尽管我不记得你可以制作 SELECT 触发器)...