如何在 Java EE 环境中使用 JMS 和 JPA 处理事务回滚?
How to handle transaction rollback with JMS and JPA in a Java EE environment?
CMT MDB 的默认回滚行为是return将消息发送到目的地,以便再次处理它。
是否可以避免重新传送由托管 MDB 处理的消息,即使事务已回滚? (或者可能配置容器处理的确认行为)。
到目前为止,我想到了以下备选方案:
- 将业务事务与消息事务隔离 - 我可以在业务方法上使用
TransactionAttributeType.REQUIRES_NEW
但它创建了一个业务场景
事件可能被处理两次,因为消息可能潜在
业务交易成功后不确认。
- 使用 BMT - 与上述相同的问题,因为事务将与消息事务分开。
- 使用 JMS 服务器专有配置处理传送失败 - 如果可能,我想将此逻辑保留在应用程序中。
此外,我必须为所有队列处理它,因为 WebLogic 默认
配置是永远重新传递消息。
阅读后 this tutorial 我仍然不确定如何解决这个问题,但到目前为止,使用专有的 WebLogic 控制台控制消息传递失败似乎是正确的选择。在这种情况下,对队列的重新交付设置限制 - 例如:3 次尝试。它会产生处理开销,因为无效的业务事件可能会失败 3 次,但我可以保证系统完整性。
大家怎么看?
详情
我有一个与业务事务集成的 MDB,它使用 JPA(WebLogic 10.3.6 中的 EclipseLink)。一切都是 运行 CMT,交易是分布式的。事务和消息确认由容器控制。
如果 JPA 提供程序中发生异常(例如:非空列的空值),则将重新传送消息,因为提供程序正在回滚事务并且消息未被确认。我是否捕捉到异常并不重要,EclipseLink 无论如何都会回滚事务。我知道这是 JPA 的正确行为。
另外,使用 MessageDrivenContext.getRollbackOnly()
returns false。我希望这是真的。
如果我使用 TransactionAttributeType.REQUIRES_NEW
执行我的业务方法,事务将回滚并且不会重新传递消息,但是消息处理事务将是独立的,这也是不希望的。我确实设置了一个 JDBC 存储来将消息保存在数据库中。
我会留下一些假人 类 来说明我的观点。
MDB消息处理
提取有效负载后,我将其转发到会话 bean 以处理持久性逻辑。
public void onMessage(Message message) {
try {
// Extract the payload
TextMessage txtMsg = (TextMessage) message;
String employeeName = txtMsg.getText();
// Call service
service.createEmployee(employeeName);
} catch (Exception e) {
e.printStackTrace();
} finally {
// When the JPA provider rollbacks back the transaction, this value
// is still "false"
log.info(String.format("Rollback only: [%s]", mdContext.getRollbackOnly()));
}
}
强制 JPA 提供程序异常
通过在非空字段中留下 null
强制错误。
// Message and business will run in the same transaction
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public void createEmployee(String name) {
Employee employee = new Employee();
employee.setName(null); // Null value to force constraint error
try {
// This part triggers the exception within the JPA provider, and the
// Java EE transaction is rolledback and forces the JMS message to be
// redelivered.
em.persist(employee);
} catch (Exception e) {
// Capturing the exception does not affect the rollback behavior
e.printStackTrace();
}
}
这是 EclipseLink 抛出的错误。它包含在 RuntimeException
中,因此它是一个系统异常,事务将回滚。
javax.ejb.EJBTransactionRolledbackException: EJB Exception: ; nested exception is: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.1.v20111018-r10243): org.eclipse.persistence.exceptions.DatabaseException
您使用的是什么 Q 提供程序?
您似乎已经涵盖了各种可能性
恕我直言,最好的解决方案是将使用 JPA 的业务逻辑隔离在具有 "Transaction new" 属性的单独外观 SLSB 中,并仍然使用 CMT 语义,因此如果在此 SLSB 中发生不良情况,您仍然可以提交 tx,其中 onMessage tx 运行 由您的 Application Server
自动启动
这听起来与您的第一个解决方案相似。
我的第二个选择是配置 Q 管理器,这样当消息在 Q 上 "redelivered" 一定次数时(即由于 tx 回滚而放回 Q),消息被移动到另一个 Q(死信队列)(并在此 Q 上配置一些 monitoring/alerting 系统...
Denis(JMSToolBox 的作者)
恕我直言,有 2 笔交易正是您要找的。
有一个事务处理由 EJB 容器启动的 MDB,然后在 MDB 中调用无状态会话 bean (SLSB) 中的业务方法,该方法用 Transaction.REQUIRES_NEW 注释,因此您的业务逻辑处理数据库的事务在其自己的隔离事务中被隔离。
当您从 SLSB 的方法返回时,无论您的业务逻辑方法中发生了什么,MDB tx 都将在 MDB 的 onMessage() 方法结束时由容器提交,消息将是consumed/removed 来自Q. 重新加工的可能性为零...
唯一的例外是 MDB 本身出现问题,但您可以轻松处理
干杯
CMT MDB 的默认回滚行为是return将消息发送到目的地,以便再次处理它。
是否可以避免重新传送由托管 MDB 处理的消息,即使事务已回滚? (或者可能配置容器处理的确认行为)。
到目前为止,我想到了以下备选方案:
- 将业务事务与消息事务隔离 - 我可以在业务方法上使用
TransactionAttributeType.REQUIRES_NEW
但它创建了一个业务场景 事件可能被处理两次,因为消息可能潜在 业务交易成功后不确认。 - 使用 BMT - 与上述相同的问题,因为事务将与消息事务分开。
- 使用 JMS 服务器专有配置处理传送失败 - 如果可能,我想将此逻辑保留在应用程序中。 此外,我必须为所有队列处理它,因为 WebLogic 默认 配置是永远重新传递消息。
阅读后 this tutorial 我仍然不确定如何解决这个问题,但到目前为止,使用专有的 WebLogic 控制台控制消息传递失败似乎是正确的选择。在这种情况下,对队列的重新交付设置限制 - 例如:3 次尝试。它会产生处理开销,因为无效的业务事件可能会失败 3 次,但我可以保证系统完整性。
大家怎么看?
详情
我有一个与业务事务集成的 MDB,它使用 JPA(WebLogic 10.3.6 中的 EclipseLink)。一切都是 运行 CMT,交易是分布式的。事务和消息确认由容器控制。
如果 JPA 提供程序中发生异常(例如:非空列的空值),则将重新传送消息,因为提供程序正在回滚事务并且消息未被确认。我是否捕捉到异常并不重要,EclipseLink 无论如何都会回滚事务。我知道这是 JPA 的正确行为。
另外,使用 MessageDrivenContext.getRollbackOnly()
returns false。我希望这是真的。
如果我使用 TransactionAttributeType.REQUIRES_NEW
执行我的业务方法,事务将回滚并且不会重新传递消息,但是消息处理事务将是独立的,这也是不希望的。我确实设置了一个 JDBC 存储来将消息保存在数据库中。
我会留下一些假人 类 来说明我的观点。
MDB消息处理
提取有效负载后,我将其转发到会话 bean 以处理持久性逻辑。
public void onMessage(Message message) {
try {
// Extract the payload
TextMessage txtMsg = (TextMessage) message;
String employeeName = txtMsg.getText();
// Call service
service.createEmployee(employeeName);
} catch (Exception e) {
e.printStackTrace();
} finally {
// When the JPA provider rollbacks back the transaction, this value
// is still "false"
log.info(String.format("Rollback only: [%s]", mdContext.getRollbackOnly()));
}
}
强制 JPA 提供程序异常
通过在非空字段中留下 null
强制错误。
// Message and business will run in the same transaction
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public void createEmployee(String name) {
Employee employee = new Employee();
employee.setName(null); // Null value to force constraint error
try {
// This part triggers the exception within the JPA provider, and the
// Java EE transaction is rolledback and forces the JMS message to be
// redelivered.
em.persist(employee);
} catch (Exception e) {
// Capturing the exception does not affect the rollback behavior
e.printStackTrace();
}
}
这是 EclipseLink 抛出的错误。它包含在 RuntimeException
中,因此它是一个系统异常,事务将回滚。
javax.ejb.EJBTransactionRolledbackException: EJB Exception: ; nested exception is: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.1.v20111018-r10243): org.eclipse.persistence.exceptions.DatabaseException
您使用的是什么 Q 提供程序?
您似乎已经涵盖了各种可能性
恕我直言,最好的解决方案是将使用 JPA 的业务逻辑隔离在具有 "Transaction new" 属性的单独外观 SLSB 中,并仍然使用 CMT 语义,因此如果在此 SLSB 中发生不良情况,您仍然可以提交 tx,其中 onMessage tx 运行 由您的 Application Server
自动启动这听起来与您的第一个解决方案相似。
我的第二个选择是配置 Q 管理器,这样当消息在 Q 上 "redelivered" 一定次数时(即由于 tx 回滚而放回 Q),消息被移动到另一个 Q(死信队列)(并在此 Q 上配置一些 monitoring/alerting 系统...
Denis(JMSToolBox 的作者)
恕我直言,有 2 笔交易正是您要找的。
有一个事务处理由 EJB 容器启动的 MDB,然后在 MDB 中调用无状态会话 bean (SLSB) 中的业务方法,该方法用 Transaction.REQUIRES_NEW 注释,因此您的业务逻辑处理数据库的事务在其自己的隔离事务中被隔离。
当您从 SLSB 的方法返回时,无论您的业务逻辑方法中发生了什么,MDB tx 都将在 MDB 的 onMessage() 方法结束时由容器提交,消息将是consumed/removed 来自Q. 重新加工的可能性为零...
唯一的例外是 MDB 本身出现问题,但您可以轻松处理
干杯