我如何处理mdb内的异常?我有一种有趣的感觉,异常发生在try catch块之后,所以我无法捕获并记录它。 Glassfish v3决定重复整个信息。它会进入一个无限循环,并在硬盘上写入很多日志文件。
我正在使用Glassfishv3.01 + Eclipselink 2.0.1
public class SaveAdMessageDrivenBean implements MessageListener {
@PersistenceContext(unitName="QIS")
private EntityManager em;
@Resource
private MessageDrivenContext mdc;
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage obj = (ObjectMessage)message;
AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
save(alyzres);
}
} catch (Throwable e) {
mdc.setRollbackOnly();
log.log(Level.SEVERE, e);
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
Some s = em.find(Some.class, somepk);
s.setSomeField("newvalue");
// SQL Exception happens after leaving this method because of missing field for ex.
}
}
答案 0 :(得分:5)
你的消息中毒情况很糟糕......
我看到的主要问题是:
save()
中的onMessage()
方法:这意味着容器无法为save
方法注入正确的事务处理代理save()
方法都应该有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
才能在单独的事务中提交,否则它将加入onMessage
事务(默认为REQUIRED
)并绕过您的异常处理代码,在成功执行onMessage
我要做的是:
将save
方法移动到新的无状态会话bean:
@Stateless
public class AnalyzerResultSaver
{
@PersistenceContext(unitName="QIS")
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
Some s = em.find(Some.class, somepk);
s.setSomeField("newvalue");
// SQL Exception happens after leaving this method
}
}
在MDB中注入此bean:
public class SaveAdMessageDrivenBean implements MessageListener {
@Inject
private AnalyzerResultSaver saver;
@Resource
private MessageDrivenContext mdc;
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage obj = (ObjectMessage)message;
AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
saver.save(alyzres);
}
} catch (Throwable e) {
mdc.setRollbackOnly();
log.log(Level.SEVERE, e);
}
}
}
另一个提示:在此代码中,消息中毒仍然存在。现在它来自调用mdc.setRollbackOnly();
的行。
我建议在这里记录异常并将消息传输到毒性队列,从而阻止容器无限次地重新提交消息。
<强>更新强>
“毒性队列”或“错误队列”只是保证您(希望可恢复的)丢弃的消息不会完全丢失的一种手段。它在集成场景中被大量使用,其中无法保证消息数据的正确性。
设置中毒队列意味着定义目标队列或主题,并将“错误”消息重新发送到此目标。
操作员应定期检查此队列(通过专用应用程序)并修改消息并重新提交到“正常”队列,或丢弃该消息并要求重新发送。
答案 1 :(得分:2)
我相信您发布的代码大部分都可以。
您使用
@TransactionAttribute(TransactionAttributeType.REQUIRED)
完全被忽略,因为此(和大多数其他)注释只能应用于业务方法(包括onMessage)。但这并不重要,因为你的onMessage方法是免费获得一个隐式的。
这导致消息处理在Java EE容器中是事务性的。如果交易因任何原因失败,则要求容器再次尝试发送消息。
现在,您的代码正在从save方法中捕获异常,这很好。但是,您明确标记了事务以进行回滚。这会告诉容器邮件传递失败并且应该再次尝试。
因此,如果删除:
mdc.setRollbackOnly();
容器将停止尝试重新发送消息。
答案 2 :(得分:1)
如果我没有弄错,你就让容器处理交易。这样,实体管理器将对方法完成后将刷新的操作进行排队,这就是为什么在方法完成后会出现异常的原因。
直接使用em.flush()
作为方法的最后一步将执行事务的所有相关查询,抛出异常,而不是在稍后当容器在提交flush()
时抛出异常交易。