The need is obvious. We have to send a JMS message (via jmsTemplate) and make an insert (via hibernateTemplate).
The solution is to wrap these operations with transactionTemplate execute method.
The only flaw is that main method doesn't stop execution after all. I have to exit it manually.
What can be the reason?
Code:service.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <context:annotation-config/> <bean id="dataSourceJNDI" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.postgresql.Driver"/> <property name="url" value="jdbc:postgresql://localhost:5432/springbase?autoReconnect=true"/> <property name="username" value="webmodule"/> <property name="password" value="1234"/> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"> <property name="targetDataSource" ref="dataSourceJNDI"/> <property name="defaultAutoCommit" value="true"/> <property name="defaultTransactionIsolationName" value="TRANSACTION_SERIALIZABLE"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="dataSource"/> </bean> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <constructor-arg ref="sessionFactory"/> </bean> <bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <!-- when close is called, should we force transactions to terminate or not? --> <property name="forceShutdown" value="true"/> </bean> <!-- Also use Atomikos UserTransactionImp, needed to configure Spring --> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300"/> </bean> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager"/> <property name="userTransaction" ref="atomikosUserTransaction"/> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!--<property name="configurationClass" value="org.hibernate.cfg.Configuration"/>--> <property name="configLocation" value="/org/coursera/hibernate.cfg.xml"/> <property name="hibernateProperties"> <props> <!--<prop key="current_session_context_class">org.hibernate.context.ThreadLocalSessionContext</prop>--> <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop> <!--<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>--> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.connection.url">jdbc:postgresql://localhost:5432/springbase?autoReconnect=true </prop> <prop key="hibernate.connection.username">webmodule</prop> <prop key="hibernate.connection.password">1234</prop> <prop key="hibernate.c3p0.minPoolSize">5</prop> <prop key="hibernate.c3p0.maxPoolSize">20</prop> <prop key="hibernate.jdbc.batch_size">20</prop> <prop key="hibernate.connection.autocommit">false</prop> <prop key="hibernate.">false</prop> </props> </property> <property name="dataSource" ref="dataSource"/> </bean> <bean id="connectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory"> <!-- <property name="brokerURL" value="vm://localhost?broker.persistent=false"/>--> <property name="brokerURL" value="tcp://localhost:61616"/> <property name="redeliveryPolicy.maximumRedeliveries" value="-1"/> <property name="redeliveryPolicy.redeliveryDelay" value="5000"/> <property name="redeliveryPolicy.initialRedeliveryDelay" value="0"/> </bean> <bean id="XAConnectionFactory" class="com.atomikos.jms.AtomikosConnectionFactoryBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="amq1"/> <property name="xaConnectionFactory" ref="connectionFactory"/> </bean> <bean id="logQueue" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="log.queue"/> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="XAConnectionFactory"/> <property name="receiveTimeout" value="10000"/> <property name="sessionTransacted" value="true"/> </bean> </beans>
Code:public class MainHibernate { private static HibernateTemplate hibernateTemplate; private static TransactionTemplate txTemplate; private static JmsTemplate jmsTemplate; private static Queue queue; static { ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"service.xml"}); hibernateTemplate = context.getBean("hibernateTemplate", HibernateTemplate.class); txTemplate = context.getBean("transactionTemplate", TransactionTemplate.class); jmsTemplate = context.getBean("jmsTemplate", JmsTemplate.class); queue = context.getBean("logQueue", Queue.class); } public static void main(String[] args) { txTemplate.execute(new TransactionCallback<Void>() { public Void doInTransaction(TransactionStatus txStatus) { try { send(); System.out.println("Has already sent"); save(); } catch (RuntimeException e) { txStatus.setRollbackOnly(); throw e; } return null; } }); } public static void send() { jmsTemplate.send(queue, new MessageCreator() { public Message createMessage(Session session) throws JMSException { MapMessage target = session.createMapMessage(); target.setString("message", "hello"); return target; } }); } public static void save() { Foo foo = new Foo("fuuu"); hibernateTemplate.saveOrUpdate(foo); System.out.println(foo); } }
Unless it works fine I would still like to know if it is the optimal way to manage distributed transactions. Is there any possibility to do the same without transactionTemplate?
And one more thing. Imagine there is some service class. Some of it methods require distributed transactions and some do not. How can I on the method level vary the transactionManager?


Reply With Quote
