简短的问题:有没有办法强制无状态 EJB 调用的 POJO 存在于 EJB 的上下文中,以便事务和资源注入可以在 POJO 中工作?
具体来说,在我想要做的事情的上下文中:如何在 EJB 的事务中包含 POJO JMS 生产者,该生产者在调用 POJO 发送消息之前将一些数据保存在数据库中,这样如果消息不能由于异常发送,数据库事务也会回滚吗?我想异步发送邮件。
这是一条幸福的道路(从无状态会话 bean 开始):
- 将数据保存到数据库 // 这有效
- 从持久化的数据中提取选择数据并将其放入
自定义“消息”类(实际上是 dto)
- 调用 EmailQueueMessenger POJO 的 sendEmail 方法,向其传递消息对象。
- 消息被发送到 MDB 来处理并发送电子邮件(不是问题的一部分,只是为了完整性而放在这里)
下面的代码有效,如果我在上下文查找中强制发生错误,它不会回滚调用类中的数据库“持久化”。顺便说一句,我也无法让 @Resource 注入工作。
//In the EJB
EmailQueueMessenger eqm = new EmailQueueMessenger();
eqm.sendEmail(messageObject);
// mailObject will be translated into an email message at the other end of the queue.
/******************** POJO Below ************/
public class EmailQueueMessenger implements Serializable {
// Resource injection doesn't work... using 'lookup' below, which does work.
// @Resource(name = "jms/EmailerQueueConnectionFactory")
// private ConnectionFactory connectionFactory;
// @Resource(name = "jms/EmailerQueue")
// private Destination EmailerQueue;
public EmailQueueMessenger() {
}
public void sendEmail(MailMessageDTO theMessage) {
Context ctx = null;
try {
ctx = new InitialContext();
ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/EmailerQueueConnectionFactory");
System.out.println("JMS Producer CTX Name In Namespace: " + ctx.getNameInNamespace());
//Destination EmailerQueue = (Destination) ctx.lookup("jms/ERROR"); // forces exception
Destination EmailerQueue = (Destination) ctx.lookup("jms/EmailerQueue"); // normal working code
try {
Connection con = connectionFactory.createConnection();
Session session = con.createSession(false,
Session.AUTO_ACKNOWLEDGE);
MessageProducer msgProd = session.createProducer(EmailerQueue);
...
我尝试添加:
@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless
POJO 定义,但没有什么区别。
FWIW 我正在为 EmailQueueMessenger 使用单独的类,因为应用程序的其他部分需要偶尔发送电子邮件,因此不想重复代码。
应该提到的是,我做了一个测试,将所有 JMS 内容移至第一个 EJB 中,并且它运行正确......但我需要它在一个单独的类中工作,以供应用程序的其他部分使用。
我认为你有两个问题:
你需要让你的 pojo 成为 SLSB。它应该被注入到您的 jms 侦听器中,而不是直接调用,以便您处理代理引用。它仍然可以作为简单的 pojo 重用,因为如果未部署在容器中,注释将被忽略。
您正在使用 AUTO_ACKNOWLEDGE 创建 jms 会话,但需要对其进行事务处理。此外,请确保 jms 连接来自事务性 JCA 源,因为这会将会话与事务关联起来。
=========更新=========
嘿比尔;
抱歉,出于某种原因,我认为外部 bean 是 JMS 侦听器......无论如何,问题是相同的。
如果你想电子邮件队列信使要按照您在其上放置的注释(事务、注入等)进行操作,您必须将其作为 EJB 引用,而不是作为简单的 pojo。因此,您的外部会话 bean 应该如下所示:
@EJB // key difference
private EmailQueueMessenger eqm;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void sendMessage(Object messageObject) {
eqm.sendEmail(messageObject);
}
现在你的
@Resource(name = "jms/EmailerQueueConnectionFactory")
@Resource(name = "jms/EmailerQueue")
and
@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless
注释将受到尊重。
最后,您的 JMS 发送方将在调用时注册事务,并且您需要确保事务管理器知道您正在事务中登记第二个资源管理器(首先是 DB,现在是 JMS)。我对 glassfish 不太熟悉,但似乎有一个带有开关的配置屏幕,允许您指定连接工厂的事务支持级别 https://stackoverflow.com/questions/4186738/glassfish-jta-does-not-affect-jms-resource-rollback-but-message-sent.
我会将发件人代码更改为:
Session session = con.createSession(true, Session.SESSION_TRANSACTED);
从技术上讲,您可以将 JMS 连接实例缓存在 EmailQueueMessenger 实例中。您的代码不应关闭 JMS 会话,因为这将在事务完成时进行处理(尽管我已经看到了 JMS/JTA 实现之间在这一点上的差异)。
我希望这能澄清一切,我really希望它有效!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)