我已经将服务实现为无状态会话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
”注释一个方法。所以我想这可能只是在用'成功'调用@AfterCompletion时编写lucene索引的解决方案。但是无状态会话EJB不允许使用此注释。我应该简单地将EJB类型从无状态更改为有状态吗?但是,这对使用仍然是无状态会话EJB的BusinessService-EJB影响我的场景有何影响?这会有用吗?我还担心将我的ejb形式无状态改为有状态会对性能产生影响。
或者还有其他方法可以解决这个问题吗?例如,监听器根据事务ID编写日志,并在事务完全完成后更新lucene索引....
2018-08-29 - 解决方案:
我通过以下方式解决了这个问题:
我没有在我的方法DataService.save(data)中直接更新Lucene索引,而是使用相同的transcation创建一个带有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也不会包含“未刷新的”文档。此行为等于事务模型“Read Committed”。
另见类似的讨论:How making stateless session beans transaction-aware?
如果lucene index不是transactionnal,则在发生故障时将无法回滚。
或者还有其他方法可以解决这个问题吗?例如,监听器根据事务ID编写日志,并在事务完全完成后更新lucene索引....
如果索引更新失败怎么办?
整个事务必须在单个事务中,并且只有在事务中的每个操作都成功时才必须提交事务。这是保证数据一致性的唯一方法。
编写一些测试来检查当您使用@Transactional或使用UserTransaction注释调用方法时事务如何反应。
我通过以下方式解决了这个问题:
我没有在我的方法DataService.save(data)中直接更新Lucene索引,而是使用相同的transcation创建一个带有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也不会包含“未刷新的”文档。此行为等于事务模型“Read Committed”。
另见类似的讨论:How making stateless session beans transaction-aware?