【问题标题】:Write entity bean contents to db before transaction commits在事务提交之前将实体 bean 内容写入 db
【发布时间】:2014-01-10 18:06:17
【问题描述】:

我正在使用 GlassFish v2ur1(这是我们公司使用的,我目前无法升级它)。我有一个从计时器定期调用的 EJB (ejbA)。在调用中,我正在读取一个文件,为每一行创建一个实体 bean,并将实体 bean 持久化到一个数据库(PostgreSQL v9.2)。在调用 entitymanager.persist(entityBean) 之后,对 servlet 进行 HTTP 调用,传递 entityBean 的 ID,然后调用另一个 EJB (ejbB)。 ejbB 向另一个实体 bean ejbC 发送 JMS 消息。这是一个生产系统,我必须进行 HTTP 调用,它会进一步处理数据。 ejbC 与 ejbA 在同一个企业应用程序中,但使用不同的 EntityManager。 ejbC接收到id,从db中读取记录,修改记录,持久化。

我遇到的问题是实体 bean 的数据在计时器调用的事务完成之前不会存储到数据库中(见下文)(我知道这是 EJB 的工作方式)。调用 ejbB 时,它无法在 db 中找到具有它接收到的 id 的记录。我尝试了几种方法将数据存储到数据库中,以便 ejbC 可以找到它:

1) 在 ejbA 中持久化 entityBean 时,我尝试将刷新模式设置为 COMMIT:

- em.setFlushMode(FlushModeType.COMMIT)
- instantiate entity bean
- em.persist(entityBean)
- em.flush()

但是,结果是一样的,在调用 ejbC 时,db 中没有记录。

2) 我创建了 ejbD 并在其中添加了一个带有 TransactionAttributeType.REQUIRES_NEW 的 storeRecord 方法(它持久化 entityBean)。这应该暂停 ejbA 的事务,启动 ejbD 的事务,提交它,然后恢复 ejbA 的事务。同样,这里的结果是相同的,在调用 ejbC 时,数据库中没有记录。我还看到此解决方案存在问题,当我调用 storeRecord 方法时,ejbA 调用刚刚停止。没有抛出异常,但即使有更多行,我也看不到 EJB 正在处理文件中的任何行。它似乎中止了 EJB 调用并在没有任何迹象的情况下回滚了事务。不确定这是否是 GlassFish v2ur1 错误。

如何确保数据存储在 ejbA 中的 db 中,以便在调用 ejbC 时,它可以找到 db 中的记录?顺便说一句,ejbA 中还有其他事情我不一定要提交。我想将我尝试存储到数据库中的 entityBeans 持久化。

ejbA

ejbTimer called (txn starts)
read file contents
for each line
   create entity bean
   persist entity bean to db
   make HTTP call to ejbB, passing id
   <see ejbC>
return (txn ends)

ejbB

Processes data based on id
Looks up JMS queue for ejbC
Passes ejbC the id

ejbC

ejb method called (txn starts)
read record based on received id
modify record and persist
return (txn ends)

【问题讨论】:

    标签: java postgresql transactions glassfish ejb


    【解决方案1】:

    当使用“已提交”的事务隔离时,没有其他事务可以看到未提交事务所做的更改。您可以指定较低的事务隔离,但这对 PostgreSQL 没有影响:它的“最混乱”行为是读取提交的,因此您根本无法使用 PostgreSQL 来做到这一点。 你也不应该

    ejbA 应该通过 HTTP 调用 ejbB。 Servlet 应该只用于服务远程客户端请求,而不是提供内部服务。 ejbA 应该直接连接和调用 ejbB。如果ejbB中的方法注解TransactionAttributeType.MANDATORYTransactionAttributeType.REQUIRED,ejbB会看到ejbA创建的实体,因为它在同一个事务下。

    在 ejbB 中,不需要持久化:只需使用 EntityManager 加载实体并进行更改。

    如果你完全受制于这种 HTTP 机制,你可以使用 bean 管理的事务,但这是一种糟糕的做事方式:

    read file contents
    for each line
        start transaction
        create entity bean
        persist entity bean to db
        commit transaction
        make HTTP call
    

    【讨论】:

    • 格伦,感谢您的回复。我更改了代码以向 ejbB 添加一个远程接口,ejbA 现在调用(在持久化 entityBean 之后)而不是使用 HTTP。但是,我看到的行为是 ejbB 找不到持久化的 entityBean ejbA,所以它创建了一个新的。我给出的例子被简化了。 ejbA 在持久化 entityBean 后调用 ejbB。但是 ejbB 向另一个 EJB 发送 JMS 消息,将其称为 ejbD,并且 ejbD 执行 entityBean 的查找,如果未找到则创建一个新的。我认为这不会改变任何事情,因为它仍然在同一个事务中执行。
    • 最好是更新示例以包含 ejbD 和 JMS、创建一个新问题,还是只在评论中留下信息?
    • 如果 ejbD 被 JMS 调用(例如,它是一个消息驱动的 bean),ejbD 将不会在与 ejbB 相同的事务中运行。它可能会在 ejbB 完成之前运行。听起来 ejbD 正在做的任何工作都应该直接调用,而不是通过 JMS 延迟。
    • 我明白了。我不能直接调用 ejbD。调用 ejbB 时需要先完成大量额外的工作。关于如何在 ejbA 中提交 entityBean 的任何建议,以便在调用 ejbB->ejbD 并且 ejbD 查询它时会找到它?上面的示例是我尝试解决的方法,但不起作用。
    【解决方案2】:

    我做了两件事来解决这个问题。 1) 我向 ejbB 添加了远程方法,这些方法执行与 HTTP 调用相同的功能。这样对 ejbB 的调用就在同一个事务中。

    Glenn Lane 指出的问题的根源是事务在从 ejbA 到 ejbB 的调用中继续,但是当 ejbB 将 JMS 消息发送到 ejbC 时它结束了……事务没有扩展到打电话给ejbC。这意味着当 id 到达 ejbC 时,它处于一个新事务中,无法看到 ejbA 保存到数据库的数据。

    2) 我将实体 bean 以特殊状态存储到 ejbA 中的 db 中。当对 ejbA 的计时器调用返回(因此 txn 提交)时,实体 bean 将被存储。当计时器再次调用 ejbA 时,它会在数据库中以这种特殊状态查找记录。然后它调用 ejbB。 ejbB 发送一条 JMS 消息。当 ejbC 获得 id 时,它会在 db 中找到记录(因为它之前在之前的 txn 中提交过),更改它的状态,然后继续处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-31
      • 2015-01-10
      • 2011-10-08
      • 1970-01-01
      • 2018-02-20
      • 2012-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多