为什么Hibernate会抛出org.hibernate.exception.LockAcquisitionException异常?

16

我有这个方法:

mymethod(long id){  
    Person p = DAO.findPerson(id);

    Car car = new Car();
    car.setPerson(p);
    p.getCars().add(car);

    DAO.saveOrUpdate(car);
    DAO.saveOrUpdate(p);
    DAO.delete(p.getCars().get(0));//A person have many cars
}  

映射:

Person.hbm.xml

<!-- one-to-many : [1,1]-> [0,n] -->
<set name="car" table="cars" lazy="true" inverse="true">
    <key column="id_doc" />
    <one-to-many class="Car" />
</set>

<many-to-one name="officialCar"
class="Car" 
column="officialcar_id" lazy="false"/>  

Cars.hbm.xml

<many-to-one name="person" class="Person"
            column="id_person" not-null="true" lazy="false"/>   

这个方法对于单个线程表现良好,但在多个线程上运行时会出现错误:

02/08/2014 - 5:19:11 p.m. - [pool-1-thread-35] - WARN - org.hibernate.util.JDBCExceptionReporter - SQL Error: 60, SQLState: 61000 
02/08/2014 - 5:19:11 p.m. - [pool-1-thread-35] - ERROR - org.hibernate.util.JDBCExceptionReporter - ORA-00060: deadlock detection while waiting for a resource 
 
02/08/2014 - 5:19:11 p.m. - [pool-1-thread-35] - WARN - org.hibernate.util.JDBCExceptionReporter - SQL Error: 60, SQLState: 61000 
02/08/2014 - 5:19:11 p.m. - [pool-1-thread-35] - ERROR - org.hibernate.util.JDBCExceptionReporter - ORA-00060: deadlock detection while waiting for a resource 
 
02/08/2014 - 5:19:11 p.m. - [pool-1-thread-35] - ERROR - org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session 
org.hibernate.exception.LockAcquisitionException: Could not execute JDBC batch update

AOP事务:

<tx:advice id="txAdviceNomService" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
        <tx:method name="getAll*" read-only="true" propagation="SUPPORTS" />
        <tx:method name="find*" read-only="true" propagation="SUPPORTS" />
    </tx:attributes>
</tx:advice>

NB: 当我在更新后添加 Thread.sleep(5000) 时,一切正常。但这种解决方法不够干净。


我使用REQUIRED传播。如果我使用REQUIRES_NEW,我得到的结果是一样的。 - Abdelhafid
2个回答

16

根据您的映射,操作序列应该如下所示:

Person p = DAO.findPerson(id);

Car car = new Car();
car.setPerson(p);

DAO.saveOrUpdate(car);

p.getCars().add(car);

Car firstCar = p.getCars().get(0);
firstCar.setPerson(null);
p.getCars().remove(firstCar);
if (p.officialCar.equals(firstCar)) {
   p.officialCar = null;
   p.officialCar.person = null;
}

DAO.delete(firstCar);

更新删除操作会获取一个独占锁,即使在READ_COMMITTED隔离级别下也是如此。

如果另一个事务想要更新当前正在运行的事务已经锁定的同一行,则不会出现死锁,而是会抛出锁获取超时异常。

因为发生了死锁,这意味着您在多个表上获取了锁,并且锁获取没有正确排序。

因此,请确保服务层方法设置事务边界,而不是DAO方法。我看到您将getfind方法声明为使用SUPPORTED,这意味着它们仅在当前已启动事务时使用事务。我认为您应该对它们也使用REQUIRED,但只需将其标记为read-only = true

所以,请确保事务方面将事务边界应用于“mymethod”,而不是DAO方法。


该交易已应用于mymethod。我更改了问题的代码。 - Abdelhafid
你不需要为Person调用saveOrUpdate,而是为新车调用它。person.cars是单向的吗?如果没有设置cat.person,保存/删除时也要设置。 - Vlad Mihalcea
谢谢 Vlad。我已经更改了代码。但是我仍然遇到同样的问题。 - Abdelhafid
你可以发布 Car 和 Person 的映射吗? - Vlad Mihalcea
我也更新了我的回复。 - Vlad Mihalcea
谢谢Vlad,我会添加一个解决方案。你的代码是正确的,但我的问题与索引有关。 - Abdelhafid

3
我有汽车,每个汽车可以拥有(1-n)个位置。 在位置表中我有一个外键(id_car),但是这个外键没有被索引。 当我为这个外键添加了一个索引之后,我的问题得到了解决。
请参考这个答案

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接