【发布时间】:2016-11-18 10:03:54
【问题描述】:
在我的应用程序中,我遇到了死锁问题,不知道是否以及如何处理它。 我在 glassfish 服务器上使用 JPA 2.1(带有 eclipselink)。
有两个 EJB。 OuterBean 应该将LogEntry 写入数据库并在循环中调用InnerBean。 InnerBean自己
应该写一个LogEntry 并做一些其他的事情(对其他实体进行更多的数据库操作)。的调用
InnerBean#execute() 彼此独立,这意味着如果一种方法失败(回滚)其他方法应该
继续运行。因此InnerBean#execute() 在自己的事务中运行。
执行下面的代码时,我得到一个java.sql.SQLException: Lock wait timeout exeeded; try restarting transaction
在 MySQL 和 java.sql.SQLSyntaxErrorException: ORA-02049: timeout: distributed transaction waiting for lock 下
在 Oracle 下(Postgres 只是等待永恒;可能是因为配置错误的数据库)。
我不是数据库/JPA 专家,但我猜问题是两个事务想要写入同一个数据表。我没有什么
理解是根本存在问题,因为这些数据库操作是插入应该独立于每个
其他。有没有一种方法可以实现这个用例(我是否需要使用 bean 托管事务,是否有我可以使用的注释,我可以在 em.persist(logEntry) 之后和之前强制在OuterBean#execute() 中提交循环,以便无论如何释放事务锁)?
@Stateless
public class OuterBean
{
@PersistenceContext(unitName = "PU_LOGGER")
private EntityManager em;
@EJB
private InnerBean innerBean;
public void execute()
{
LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage("OuterBean#execute()");
em.persist(logEntry);
for(int i = 0; i < 10; ++i)
{
innerBean.execute();
}
}
}
@Stateless
public class InnerBean
{
@PersistenceContext(unitName = "PU_LOGGER")
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void execute()
{
LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage("InnerBean#execute()" + Math.random());
em.persist(logEntry);
}
}
编辑
这是生成的日志
FINER: client acquired: 1990452734
FINER: TX binding to tx mgr, status=STATUS_ACTIVE
FINER: acquire unit of work: 38847372
FINEST: persist() operation called on: LogEntry{id=null, message=OuterBean#execute(), date=Mon Jul 18 09:00:27 CEST 2016}.
FINER: TX beginTransaction, status=STATUS_ACTIVE
FINEST: Connection acquired from connection pool [default].
FINEST: Execute query DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + #PREALLOC_SIZE WHERE SEQ_NAME = #SEQ_NAME")
FINEST: reconnecting to external connection pool
FINE: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [2 parameters bound]
FINEST: Execute query ValueReadQuery(name="SEQUENCE" sql="SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = #SEQ_NAME")
FINE: SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
bind => [1 parameter bound]
FINEST: local sequencing preallocation for SEQ_GEN: objects: 50 , first: 301, last: 350
FINEST: assign sequence to the object (301 -> LogEntry{id=null, message=OuterBean#execute(), date=Mon Jul 18 09:00:27 CEST 2016})
FINER: client acquired: 187184807
FINER: TX binding to tx mgr, status=STATUS_ACTIVE
FINER: acquire unit of work: 2098992041
FINEST: persist() operation called on: LogEntry{id=null, message=InnerBean#execute()0.3957184758563761, date=Mon Jul 18 09:00:28 CEST 2016}.
FINER: TX beginTransaction, status=STATUS_ACTIVE
FINEST: Connection acquired from connection pool [default].
FINEST: Execute query DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?")
FINEST: reconnecting to external connection pool
FINE: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [2 parameters bound]
【问题讨论】:
-
你在应用程序或实体上做了什么锁定?您是否检查过您的容器实际上正在启动新事务,或者是否可以忽略注释并将方法包装在更大的事务上下文中?将日志记录打开到最好,看看发生了什么,因为真的没有足够的时间继续下去。
-
嗨@Chris,我已经添加了生成的最佳日志。我不明白所有内容,但我看到将启动两个事务。这符合我的观察,如果我将
@TransactionAttribute(REQUIRES_NEW)注释留在InnerBean中,一切正常。在锁定方面,我没有显式锁定。上面的代码将通过 CDI bean 执行,没有更多的代码可以锁定任何东西。 -
看起来您需要为您的排序配置一个连接池,以便它可以在 JTA 事务之外从您的序列表中获取/分配序列号。请参阅 eclipse.org/eclipselink/documentation/2.4/concepts/… 和 stackoverflow.com/questions/19732551/…,它们展示了如何指定非 JTA 数据源
标签: jpa transactions locking eclipselink deadlock