Spring JMS - 使用注释的声明式事务管理

时间:2017-07-06 15:02:56

标签: spring-transactions spring-jms

我有一些我需要重构的旧代码,它具有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;
}

1 个答案:

答案 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
    }   
}