简短的问题:有没有办法强制由无状态 EJB 调用的 POJO 存在于 EJB 的上下文中,以便事务和资源注入(inject)将在 POJO 中工作?
特别是在我正在尝试做的事情的上下文中:我如何在 EJB 的事务中包含一个 POJO JMS 生产者,它在调用 POJO 发送消息之前将一些数据保存在数据库中,这样如果消息异常无法发送,数据库事务也会回滚?我想异步发送邮件。
这是快乐的道路(从无状态 session bean 开始):
- 将数据保存到数据库//这可行
- 从持久化的数据中提取选择数据并将其放在 自定义“消息”类(真正的 dto)
- 调用 EmailQueueMessenger POJO 的 sendEmail 方法,将消息对象传递给它。
- 消息被发送到 MDB 以处理和发送电子邮件(不是问题的一部分,只是为了完整性)
下面的代码有效,如果我在上下文查找中强制出错,它不会回滚调用类中的数据库“持久化”。顺便说一句,我也无法让@Resource 注入(inject)工作。
//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。它应该被注入(inject)到您的 jms 监听器中,而不是直接调用,以便您处理代理引用。它仍然可以作为简单的 pojo 重复使用,因为如果未部署在容器中,注释将被忽略。
您正在使用 AUTO_ACKNOWLEDGE 创建一个 jms session ,但它需要被处理。此外,请确保 jms 连接来自事务 JCA 源,因为这会将 session 关联到事务。
=========更新=========
嘿,比尔;
抱歉,出于某种原因我认为外部 bean 是 JMS 监听器......无论如何,问题是一样的。
如果您希望 EmailQueueMessenger 的行为与您放置在其上的注释一致(事务性、注入(inject)等),您必须将其作为 EJB 引用,而不是作为简单的 pojo。因此,您的外部 session 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")
和
@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless
注释将被尊重。
最后,您的 JMS 发送方将在调用时注册到事务中,您需要确保事务管理器知道您正在事务中招募第二个资源管理器(首先是 DB,现在是 JMS ).我不太熟悉 glassfish,但似乎有一个带有开关的配置屏幕,允许您指定 level of transactional support for a connection factory .
我会将发件人代码更改为:
Session session = con.createSession(true, Session.SESSION_TRANSACTED);
从技术上讲,您可以在 EmailQueueMessenger 实例中缓存 JMS 连接实例。您的代码不应关闭 JMS session ,因为这将在事务完成时处理(尽管我已经看到 JMS/JTA 实现之间在这一点上的差异)。
我希望一切顺利,我真的希望它能奏效!
关于java - 当 JMS Prod 位于辅助 POJO 类中时,如何在事务中包含 JMS Producer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10438475/