-1

我已经将服务实现为无状态会话 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 工作将按预期回滚(没有数据写入数据库)。但是在事务被取消之前,Lucene 索引现在已经针对 save() 方法的所有成功完整调用进行了更新。

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

我看到使用 Statefull Session EJB 3.2 我可以使用注释“ @AfterCompletion”来注释方法。所以我想这可能是仅当 @AfterCompletion 以“成功”调用时才编写 lucene 索引的解决方案。但是无状态会话 EJB 不允许使用此注释。我应该简单地将我的 EJB 类型从无状态更改为有状态吗?但这如何影响我的 BusinessService-EJB 场景,它仍然是无状态会话 EJB?这行得通吗?我还担心将我的 ejb 表单 stateless 更改为 statefull 会对性能产生影响。

或者有没有其他方法可以解决这个问题?例如,一个监听器根据事务 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 也不会包含“未刷新”文档。此行为等同于事务模型“已提交读”。

另请参阅类似的讨论:如何使无状态会话 bean 具有事务意识?

4

2 回答 2

0

如果 lucene 索引不是事务性的,您将无法在失败的情况下回滚。

或者有没有其他方法可以解决这个问题?例如,一个监听器根据事务 ID 编写日志,并在事务完全完成后更新 lucene 索引......?

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

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

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

于 2018-08-20T21:47:27.523 回答
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 也不会包含“未刷新”文档。此行为等同于事务模型“已提交读”。

另请参阅类似的讨论:如何使无状态会话 bean 具有事务意识?

于 2018-08-26T03:58:04.147 回答