在事务提交之前将实体bean内容写入db

时间:2014-01-10 18:06:18

标签: java postgresql transactions glassfish ejb

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

我遇到的问题是实体bean的数据没有存储到数据库中,直到来自定时器调用的事务完成(见下文)(我理解这是EJB的工作方式)。调用ejbB时,它无法在数据库中找到其收到的id的记录。我已经尝试了几种方法来将数据存储到数据库中,以便ejbC可以找到它:

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

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

然而,结果是一样的,在调用ejbC时,数据库中没有记录。

2)我创建了ejbD并使用TransactionAttributeType.REQUIRES_NEW在其中添加了storeRecord方法(持久化entityBean)。这应该暂停ejbA的事务,启动ejbD的事务,提交它,并恢复ejbA的事务。同样,这里的结果是相同的,在调用ejbC时,db中没有记录。我也看到了这个解决方案的问题,当我调用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)

2 个答案:

答案 0 :(得分:0)

当使用“read-committed”的事务隔离时,没有其他事务可以看到未提交的事务所做的更改。您可以指定较低的事务隔离,但这对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

答案 1 :(得分:0)

我做了两件事来解决这个问题。 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中提交的那样),更改它的状态,然后继续处理。