【发布时间】:2015-02-04 05:14:51
【问题描述】:
我一直在努力尝试构建一个非常简单的应用程序,该应用程序使用 spring、hibernate 和 JMS 部署到 tomee。我相信我的所有配置都是正确的(具有 mysql xa 数据源和 xa 活动 mq 连接工厂),但事情并没有像我预期的那样工作。目前我有一个简单的服务,它使用注入的实体管理器进行写入,然后在一个方法中推送到 JMS,该方法被注释为事务性(spring 注释),但我的消息侦听器在 spring 提交事务之前传递这些消息。
我尝试过直接使用 JMS 模板和 xa 连接工厂,但都不能正常工作。该模板使用从 jndi 接收的 jta 事务管理器进行配置。关于 JMS 发送的为什么不参与数据库写入的同一事务的任何想法?
弹簧配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:encryption="http://www.jasypt.org/schema/encryption"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd
http://www.jasypt.org/schema/encryption http://www.jasypt.org/schema/encryption/jasypt-spring31-encryption-1.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.1.xsd"
default-autowire="byType" default-lazy-init="false">
<context:component-scan annotation-config="false" base-package="org.superbiz" />
<bean class="org.springframework.orm.jpa.DefaultJpaDialect" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
<property name="alwaysUseJndiLookup" value="false" />
<property name="jndiFactory" >
<ref local="jndiFactory"/>
</property>
</bean>
<bean id="jndiFactory" class="org.springframework.jndi.support.SimpleJndiBeanFactory">
<property name="resourceRef" value="true" />
</bean>
<bean id="PrintTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref local="jmsFactory" />
</property>
<property name="defaultDestinationName" value="resources/jms/PrintQueue" />
<property name="deliveryPersistent" value="true"/>
<!-- <property name="sessionTransacted" value="true"/> -->
<!-- <property name="sessionAcknowledgeMode" value="0"/> -->
</bean>
<bean id="PersistTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref local="jmsFactory" />
</property>
<property name="defaultDestinationName" value="resources/jms/PersistQueue" />
<property name="deliveryPersistent" value="true"/>
<!-- <property name="sessionTransacted" value="true"/> -->
<!-- <property name="sessionAcknowledgeMode" value="0"/> -->
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
<property name="defaultPersistenceUnitName" value="movie-unit" />
<property name="persistenceContexts">
<map>
<entry key="movie-unit" value="persistence/movie-unit" />
</map>
</property>
</bean>
<context:component-scan base-package="org.superbiz.ejb" annotation-config="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<jee:jndi-lookup id="jmsFactory" jndi-name="resources/jms/ConnectionFactory" expected-type="javax.jms.ConnectionFactory" />
<tx:jta-transaction-manager />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="printBean" class="org.superbiz.mdb.PrintBean"/>
<bean id="persistBean" class="org.superbiz.mdb.PersistBean"/>
<jms:listener-container container-type="default" connection-factory="jmsFactory" cache="none" transaction-manager="transactionManager" concurrency="1" receive-timeout="1000" prefetch="-1">
<jms:listener destination="resources/jms/PrintQueue" ref="printBean" />
<jms:listener destination="resources/jms/PersistQueue" ref="persistBean" />
</jms:listener-container>
</beans>
tomee.xml(由http://tomee-openejb.979440.n4.nabble.com/MDB-doesn-t-read-messages-td4666169.html拼凑而成)
<Resource id="ActiveMQResourceAdapter" type="ActiveMQResourceAdapter">
BrokerXmlConfig=broker:(vm://localhost)
</Resource>
<Resource id="resources/jms/ConnectionFactory" type="javax.jms.ConnectionFactory">
ResourceAdapter = ActiveMQResourceAdapter
</Resource>
<Resource id="resources/jms/XAConnectionFactory" class-name="org.apache.activemq.ActiveMQXAConnectionFactory">
BrokerURL = vm://localhost
ResourceAdapter = ActiveMQResourceAdapter
</Resource>
<Resource id="resources/jms/PrintQueue" type="javax.jms.Queue"/>
<Resource id="resources/jms/PersistQueue" type="javax.jms.Queue"/>
<Resource id="MySQL Database" type="DataSource">
JdbcDriver com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
JdbcUrl jdbc:mysql://localhost/test
UserName test
</Resource>
我尝试了几种方法,包括不使用 XAConnectionFactory 并将 JMSTemplate 配置为 sessionTransacted 而不是,删除 JMSTemplate 并从 connectionFactory 创建连接/会话/生产者/消息,但我遇到了问题每一次。通过从 ConnectionFactory 手动创建连接/会话/生产者/消息,我注意到我尝试写入数据库然后发送到另一个 JMS 队列的 20 个项目在服务事务完成之前开始被读取。
据我所知,一切都配置正确(尽管我肯定是错的,因为这是从很多地方拉出来的)。我的目标是能够使用 JMSTemplate 而不是自己手动创建连接/会话/等,但我不知道为什么会发生这种情况,因此非常感谢任何想法。
我还增加了 Spring JtaTransactionManager 的日志记录,当调用 @Transactional 方法时,我看到以下情况发生
DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [org.superbiz.ejb.Movies.send]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
然后我看到我的 MDB 代码试图从 entityManager 检索项目(间歇性工作/失败)。当它成功时,我会看到这个
printing from MDB: director: director0title: title0year: 0
DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [org.springframework.jms.listener.DefaultMessageListenerContainer#0]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
当它失败时,我会看到这个
/***************** BROKEN ***************/
/*******************435265*****************/
/***************** BROKEN ***************/
DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [org.springframework.jms.listener.DefaultMessageListenerContainer#0]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
我进一步提高了日志记录
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [org.springframework.jms.listener.DefaultMessageListenerContainer#1]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
**bold** [org.springframework.jms.listener.DefaultMessageListenerContainer#0-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [org.springframework.jms.listener.DefaultMessageListenerContainer#0]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
[org.springframework.jms.listener.DefaultMessageListenerContainer#0-1] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.jms.connection.JmsResourceHolder@81032a4] for key [org.apache.activemq.ra.ActiveMQConnectionFactory@5ee0c65d] to thread [org.springframework.jms.listener.DefaultMessageListenerContainer#1-1]
[org.springframework.jms.listener.DefaultMessageListenerContainer#0-1] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.jms.connection.JmsResourceHolder@eaebd86] for key [org.apache.activemq.ra.ActiveMQConnectionFactory@5ee0c65d] to thread [org.springframework.jms.listener.DefaultMessageListenerContainer#0-1]
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [org.superbiz.mdb.PersistBean.onMessage]
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.jms.connection.JmsResourceHolder@81032a4] for key [org.apache.activemq.ra.ActiveMQConnectionFactory@5ee0c65d] bound to thread [org.springframework.jms.listener.DefaultMessageListenerContainer#1-1]
Persisted finished, but not yet committed
Leaving persist, should commit
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.interceptor.TransactionInterceptor - Completing transaction for [org.superbiz.mdb.PersistBean.onMessage]
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.jta.JtaTransactionManager - Triggering beforeCommit synchronization
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.jta.JtaTransactionManager - Triggering beforeCompletion synchronization
[org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Removed value [org.springframework.jms.connection.JmsResourceHolder@81032a4] for key [org.apache.activemq.ra.ActiveMQConnectionFactory@5ee0c65d] from thread [org.springframework.jms.listener.DefaultMessageListenerContainer#1-1]
**bold** [org.springframework.jms.listener.DefaultMessageListenerContainer#1-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
Entering print
/***************** BROKEN ***************/
/*******************597852*****************/
/***************** BROKEN ***************/
在这种情况下,DefaultMessageListenerContainer#1-1 是我的 Persist bean,0-1 是一个通过 id 检索实体然后打印内容的 bean。
我不是 100% 确定如何阅读这篇文章,但有趣的是 DMLC#1-1 在 DMLC#0-1 上开始新事务后很好地提交,但 DMLC#0-1 看到了消息.我原以为 DMLC#0-1 需要启动一个新事务才能看到这个,或者因为他收到了 JMS 消息,所以实体也应该被持久化到数据库中。
persist bean 的内容 @覆盖 @Transactional 公共无效onMessage(消息消息){ TextMessage 消息 = (TextMessage) 消息; 整数 x;
try {
x = Integer.parseInt(msg.getText());
Movie movie = new Movie("director" + x, "title" + x, x);
entityManager.persist(movie);
final long id = movie.getId();
template.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(Long.toString(id));
}
});
System.out.println("Persisted finished, but not yet committed");
System.out.println("Leaving persist, should commit");
} catch (Exception e) {
e.printStackTrace();
}
}
我的打印 bean 的内容
public void onMessage(Message message) {
System.out.println("Entering print");
final TextMessage textMessage = (TextMessage) message;
try {
long id = Long.parseLong(textMessage.getText());
Movie movie = entityManager.find(Movie.class, id);
if(movie == null){
System.out.println("/***************** BROKEN ***************/");
System.out.println("/*******************" + id + "*****************/");
System.out.println("/***************** BROKEN ***************/");
} else {
System.out.println("updating: "+ movie);
}
} catch (Exception e) {
e.printStackTrace();
}
}
整个应用程序可在https://github.com/jej2003/simple-spring 获得,运行一个普通的 Tomee 1.7.1,并将必要的休眠 jars 添加到 tomee/lib 目录。
我真的很茫然,没有人用Spring在Tomee中运行JTA事务吗?
【问题讨论】:
-
您需要在提出这样的问题时显示您的配置,并提供更多信息 - “......都不能正常工作......”而这样的 cmets 就是不这样做;对不起。此外,打开
TRACE级别的日志记录会暴露很多信息。如果您无法从TRACE日志中找出答案,请将它们发布到某个地方。
标签: spring hibernate jms spring-jms apache-tomee