我已将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?
答案 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?