在我们的环境中,我们从队列中获取大量消息并异步处理它们。对于每条消息,我们需要在数据库中创建或查找一行。我们遵循典型的create-get模式来处理两个线程正在寻址同一个" parent"对象:
public void ensure(Data d) {
try {
o = dao.create(d);
log("Created");
}
catch(Exception x) {
o = dao.get(d);
if(o != null) {
log("Existed");
}
else {
event("Couldn't create or get parent!");
}
}
}
这种方法100%有效。我总是在数据库中看到预期的对象,从来没有看到"无法创建"信息。优异。
关于这个的丑陋部分是,即使捕获并处理了对象已经存在时从create中抛出的异常,我仍然在日志中看到异常,这可以理解地造成一些混淆:
[7/16/15 11:11:37:930 EDT] 0000003b RegisteredSyn E WTRN0074E: Exception caught from before_completion synchronization operation: <openjpa-2.1.2-SNAPSHOT-r422266:1636464 fatal store error>
org.apache.openjpa.persistence.EntityExistsException: The transaction has been rolled back. See the nested exceptions for details on the errors that occurred.
<Exception stacktrace omitted>
[7/16/15 11:11:37:942 EDT] 0000003b SystemOut O Existed
抱歉,由于安全问题,我无法发布整个堆栈跟踪。
知道如何让这个幻影异常停止显示在日志中吗?
编辑以添加更多代码:
DAO:
public Data create(Data d) {
UserTransaction utx = context.getUserTransaction();
try {
utx.begin();
em.persist(d);
utx.commit();
}
catch(Exception x) {
throw MyException(x);
// also tried simply returning null
}
// also tried catching Throwable
return d;
}
答案 0 :(得分:0)
假设在事务上下文中调用ensure
(可能是因为它是EJB),只要从dao.create
抛出异常,就会收到此日志消息。
EntityManager.persist(...)中的失败导致当前事务被标记为回滚。
因此,即使您发现了异常,WebSphere也会警告您未提交您的事务。如果您对o
进行了任何更改,则不会保留这些更改。
此行为是设计使然,所有应用服务器都会执行此类操作,因为JavaEE规范要求它。基本上,任何转义EJB方法调用的java.lang.RuntimeException
都会自动标记当前事务以进行回滚。
如果您不希望发生这种情况,那么您应该在DAO中捕获它并改为抛出一个已检查的异常。
进一步阅读后修改
根据JPA规范,事务被persist操作标记为回滚。因此,您需要安排create
方法在其自己的事务中运行。通常这可以通过以下方式完成:
@TransactionAttribute(REQUIRES_NEW)
public Data create(Data d) throw MyException {
try {
em.persist(d);
} catch (PersistenceException e) {
throw new MyException(x);
}
return d;
}
由于上述原因,您仍需要检查例外。
顺便说一下,我们通常不会使用UserTransaction对象,因为默认情况下我们会获得容器管理事务(CMT)。