我有一些我需要重构的旧代码,它具有JMS的程序化事务管理。
有一个计划服务,它同步读取所有消息(使用JMS)并一次处理一个消息。我正在使用JmsTransactionManager
进行交易。我可以使用注释来使用声明式事务管理来管理每条消息的事务,而不是像以下那样以编程方式管理它们:
//code from scheduled service's run method
private void run()
{
javax.jms.Message jmsMessage = null;
do
{
TransactionStatus status = null;
try
{
status = jmsTransactionManager.getTransaction(new DefaultTransactionDefinition());
jmsMessage = jmsTemplate.receive(heldTransmissionDestination);
if(jmsMessage != null)
{
process(jmsMessage);
jmsMessage.acknowledge(); //session is still open within the transaction
}
jmsTransactionManager.commit(status);
}
catch(Exception e)
{
logger.error("Exception: ", e);
if(status != null)
{
jmsTransactionManager.rollback(status);
logger.info("JMSTransaction rollback successful");
}
//since an exception occured, break out of the do-while
break;
}
}
while(jmsMessage != null);
}
请注意,此代码有效。 jmsTemplate的配置将sessionTrasacted设置为true,如下所示:
// from config
@Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setSessionTransacted(true);
// ... other stuff ommited for brevity
return jmsTemplate;
}
答案 0 :(得分:1)
我尝试了它,它确实适用于注释。但是,有几点需要注意:
首先,在您的配置中使用@EnableTransactionManagement
(如果您使用的是纯Java DSL),如下所示:
@Configuration
@EnableTransactionManagement
public class JmsConfig {
@Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setSessionTransacted(true);
// ... other stuff ommited for brevity
return jmsTemplate;
}
@Bean
public JmsTransactionManager jmsTransactionManager() {
JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
jmsTransactionManager.setConnectionFactory(jmsConnectionFactory());
return jmsTransactionManager;
}
//other bean definitions omitted for brevity
}
或者你可以在XML配置中执行类似的操作,而不是上面的内容:
<tx:annotation-driven transaction-manager="jmsTransactionManager"/>
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory">
<ref bean="jmsConnectionFactory" />
</property>
</bean>
其次,交易的方式是proxied(AOP)。因此,需要将需要在事务中的逻辑移动到另一个类中,以便可以代理它。请注意,如果仅将事务管理器命名为transactionManager,则spring会自动检测事务管理器。但在我的情况下,我有多个这样的bean,所以我必须明确地使用这个;并在我的注释的value属性中引用它,如:
public class ServiceToSchedule
{
@Autowired
private JmsMessageProcessor jmsMessageProcessor;
public void run()
{
Message jmsMessage = null;
do
{
try {
jmsMessage = jmsMessageProcessor.readAndProcessMessage();
} catch (JMSException e) {
logger.debug("JMSException processing heldTransmission: ", e);
break;
} catch (Exception e) {
logger.debug("Exception processing heldTransmission: ", e);
break;
}
}
while(jmsMessage != null);
}
}
public class JmsMessageProcessor
{
@Transactional(value="jmsTransactionManager", propagation = Propagation.REQUIRES_NEW)
public Message readAndProcessMessage() throws JMSException
{
Message jmsMessage = jmsTemplate.receive(heldTransmissionDestination);
if(jmsMessage != null)
{
process(jmsMessage);
}
//have to return to break out of the while in the caller
return jmsMessage;
}
@Transactional(value="jmsTransactionManager", propagation = Propagation.NESTED)
protected void process(Message jmsMessage)
{
//code to process the jmsMessage, can potentially throw
//an exception that requires rolling back the jms transaction
}
}