如何对EJB3事务提交或回滚做出反应?

时间:2018-08-20 20:41:56

标签: java transactions rollback ejb-3.2

我已将Service实现为无状态会话EJB(3.2),以通过JPA存储数据。此外,每次更新数据时,EJB也会更新Lucene索引。会话bean是容器管理的。 ejb的代码如下:

@Stateless
@LocalBean
public class DataService {
    ....

    public Data save(Data myData)  {
        // JPA work....
        ...
        // update Lucene index
        ....
    }
    ....
} 

我有不同的其他BusinessService-EJB调用此DataService EJB来插入或更新数据。我无法控制BusinessService-EJB实现。由BusinessService-ejb启动的事务可以包含对DataService EJB的save()方法的多次调用。

@Stateless
@LocalBean
public class SomeBusinessService {
    @EJB
    DataService dataService;
    ....

    public void process(....)  {
        dataService.save(data1);
        ...
        dataService.save(data2);
        ....
    }
    ....
} 

如果BusinessService-EJBs方法“进程”中断,则会发生我的问题。 DataService.save()的每个方法调用都会更新给定数据对象的Lucene索引。但是,如果以后的调用之一失败,则将回滚整个事务。我的JPA工作将按预期回滚(不将任何数据写入数据库)。但是,在取消交易之前,已经针对save()方法的所有成功完全调用更新了Lucene索引。

所以我的问题是:在DataService EJB中如何应对这种情况?这有可能吗?

我看到,通过Statefull Session EJB 3.2,我可以使用“ @AfterCompletion”注释方法。因此,我猜想这可能仅在使用'success'调用@AfterCompletion时才可以编写lucene索引。但是,无状态会话EJB不允许使用此注释。 我是否应该将EJB类型从无状态更改为有状态? 但是,这如何影响我仍然使用无状态会话EJB的BusinessService-EJB的情况?这行得通吗?我还担心将我的ejb形式从无状态更改为有状态会影响性能。

还是有其他方法可以解决此问题?例如,侦听器根据事务ID编写日志,并在事务完全完成后更新lucene索引。


2018-08-29-解决方案:

我通过以下方式解决了这个问题:

我没有在我的方法DataService.save(data)期间直接更新Lucene索引,而是使用相同的事务使用JPA创建了一个新的eventLogEntry。

@Stateless
@LocalBean
public class DataService {
    ....

    public Data save(Data myData)  {
        // JPA work....
        ...
        // Write a JPA eventLog entry indicating to update Lucene index
        ....
    }
    ....
} 

现在,每当客户端调用lucene搜索方法时,我都会运行flush方法根据事件日志条目更新我的lucene索引:

@TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW)
public void flush() {
    Query q = manager.createQuery("SELECT eventLog FROM EventLog AS eventLog" );
    Collection<EventLog> result = q.getResultList();
    if (result != null && result.size() > 0) {

        for (EventLog eventLogEntry : result) {
            .... update lucen index for each entry
            .......
            // remove the eventLogEntry.
            manager.remove(eventLogEntry);
        }
    }
}

带有注释TransactionAttributeType.REQUIRES_NEW的flush()方法将仅读取已提交的eventLog条目。

因此,客户端将仅在Lucene索引中看到已提交的更新。即使在从我的一个BusinessService-EJB进行交易时,lucene也不会包含“未刷新”的文档。此行为等同于事务模型“已读”。

另请参见类似的讨论:How making stateless session beans transaction-aware?

2 个答案:

答案 0 :(得分:0)

如果Lucene索引不是事务索引,那么在失败的情况下将无法回滚。

  

还是有其他方法可以解决此问题?例如某事   就像一个侦听器根据事务ID编写日志一样,   交易完成后更新Lucene指数   完成....?

如果索引更新失败了怎么办?

整个事物必须在单个事务中,并且只有在事务中的每个操作成功之后,才必须提交该事务。这是保证数据一致性的唯一方法。

编写一些测试,以检查使用@Transactional或使用UserTransaction注释调用方方法时事务如何反应。

答案 1 :(得分:0)

我通过以下方式解决了这个问题:

我没有在我的方法DataService.save(data)期间直接更新Lucene索引,而是使用相同的事务使用JPA创建了一个新的eventLogEntry。

@Stateless
@LocalBean
public class DataService {
    ....

    public Data save(Data myData)  {
        // JPA work....
        ...
        // Write a JPA eventLog entry indicating to update Lucene index
        ....
    }
    ....
} 

现在,每当客户端调用lucene搜索方法时,我都会运行flush方法根据事件日志条目更新我的lucene索引:

@TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW)
public void flush() {
    Query q = manager.createQuery("SELECT eventLog FROM EventLog AS eventLog" );
    Collection<EventLog> result = q.getResultList();
    if (result != null && result.size() > 0) {

        for (EventLog eventLogEntry : result) {
            .... update lucen index for each entry
            .......
            // remove the eventLogEntry.
            manager.remove(eventLogEntry);
        }
    }
}

带有注释TransactionAttributeType.REQUIRES_NEW的flush()方法将仅读取已提交的eventLog条目。

因此,客户端将仅在Lucene索引中看到已提交的更新。即使在从我的一个BusinessService-EJB进行交易时,lucene也不会包含“未刷新”的文档。此行为等同于事务模型“已读”。

另请参见类似的讨论:How making stateless session beans transaction-aware?