在Oracle上使用Hibernate进行死锁事务

Ole*_*ksi 6 java oracle hibernate jpa transactions

我有以下(简化)Hibernate实体:

@Entity(name = "Foo")
public class Foo {

    @Id
    @GeneratedValue
    public int id;

    @OneToOne
    public Bar bar;
}
Run Code Online (Sandbox Code Playgroud)

和,

@Entity(name = "Bar")
public class Bar {

    @Id
    @GeneratedValue
    public int id;

    @Column
    public String field;

    @Version
    public int version;

}
Run Code Online (Sandbox Code Playgroud)

我在一个看起来大致如下的事务中更新这些实体:

Bar bar = findBar(em);
Foo foo = findFoo(em);

bar.field = "updated value";

if (<condition>) {
    em.remove(foo);
}

em.detach(bar);
em.merge(bar);
Run Code Online (Sandbox Code Playgroud)

请注意,em.remove(foo)有时仅调用,而bar始终更新.

我注意到ORA-00060: Deadlock detected在运行应用程序时偶尔会出现错误.转储似乎表明,两个僵持会话锁定em.merge(bar)em.remove(foo),但我不明白为什么会是这样.

这段代码怎么会死锁?有没有办法重组它避免死锁?

以下是跟踪中的一些额外信息:

Deadlock graph:
                       ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TX-00040005-000010dd        73    6557     X             81    6498           X
TX-00010018-000010bd        81    6498     X             73    6557           X

session 6557: DID 0001-0049-000002F5    session 6498: DID 0001-0051-0000030E 
session 6498: DID 0001-0051-0000030E    session 6557: DID 0001-0049-000002F5 

Rows waited on:
  Session 6557: obj - rowid = 00004797 - AAAEeXAB4AAADH0BBP
  (dictionary objn - 18331, file - 120, block - 12788, slot - 15)
  Session 6498: obj - rowid = 00007191 - AAAHGRAB4AAAACBBBo
  (dictionary objn - 29041, file - 120, block - 129, slot - 40)

----- Information for the OTHER waiting sessions -----
Session 6498:
program: JDBC Thin Client
    application name: JDBC Thin Client, hash value=2546894660
  current SQL:

delete from Foo where id=:1 

----- Current SQL Statement for this session (sql_id=sfasdgasdgaf) -----
update Bar set field=:1, version=:2 where id=:3 and version=:4 
Run Code Online (Sandbox Code Playgroud)

ibr*_*041 5

通常,Oracle中出现死锁的原因主要有两个

  • 所谓的SX到SSX锁升级.这个是由FK(子表)上缺少索引引起的.在这种情况下,Oracle必须在验证约束之前锁定整个子表.见AskTom Artice
  • SQL语句顺序的顺序错误

在所有情况下,死锁都是由应用程序错误引起的.您将需要来自数据库服务器的死锁报告(.trc文件).你会发现涉及SQL语句和表.由于您使用Hibernate几乎无法预测SQL语句执行的顺序,有时它可能有助于扩展实体管理器缓存,以防止过早调用flush().

编辑:好的,所以你有TX(X)锁.这些是行级,而SSX是表级.然后,死锁对象可以是表中的行,也可以是索引中的唯一键.跟踪文件还应包含每个会话的前一个语句以及游标(SQL语句执行的位置),游标还应包含绑定变量的值.

尝试执行:

select * from Foo where rowid = 'AAAHGRAB4AAAACBBBo';
select * from Bar where rowid = 'AAAEeXAB4AAADH0BBP';
Run Code Online (Sandbox Code Playgroud)
  • 你真的使用CamelCase表名吗?
  • 什么是"Foo"和"Bar"的DDL?
  • 当你在Foo和Bar之间放弃FK时会发生死锁吗?
  • 当你只打电话的时候em.remove(foo);还会把儿童酒吧删掉吗?


Ser*_*age 2

如果我正确理解了分离,你应该这样做:

Foo foo = findFoo(em);
Bar bar = findBar(em);

if (<condition>) {
    em.remove(foo);
    em.detach(bar); //If it is really necessary
    em.flush();
}

bar = findBar(em); //It will reattach the entity on the persistence context
bar.field = "updated value";

em.merge(bar); 
em.commit();
Run Code Online (Sandbox Code Playgroud)