一般的企业管理系统免不了要访问多个数据库,如框架数据库、仓库数据库等,但spring的jdbc事务只支持一个数据源的事务配置,为了在tomcat中支持多数据源事务,可以采用开源框架atomikos来进行配置。
采用的开发环境:Spring4 + hibernate4 + atomikos3.9.3 + mssql2008
1.下载atomikos-jdbc:3.9.1
所需要的包如下:
2.下载sqljdbc4.jar
有关mssql的XA支持,请参考下面:
https://technet.microsoft.com/zh-cn/library/aa342335.aspx
3.Spring atomikos事务配置
配置两个xa数据源xaDataSource,两个SessionFactory
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.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" > <context:component-scan base-package="com.framework.dao"/> <context:component-scan base-package="com.framework.dao.mssql"/> <context:component-scan base-package="com.framework.service"/> <context:component-scan base-package="com.framework.action"/> <context:component-scan base-package="com.stk.dao.mssql"/> <context:component-scan base-package="com.stk.service"/> <context:component-scan base-package="com.stk.action"/> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:framework.properties"/> </bean> <bean class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" id="datasourceFW"> <property name="uniqueResourceName"><value>framework</value></property> <property name="xaDataSourceClassName" value="com.microsoft.sqlserver.jdbc.SQLServerXADataSource" /> <property name="xaProperties"> <props> <prop key="user">${jdbc.username}</prop> <prop key="password">${jdbc.password}</prop> <prop key="URL">${jdbc.url}</prop> </props> </property> </bean> <bean id="sessionFactoryFW" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="datasourceFW"/> <!-- --> <property name="packagesToScan" value="com.framework.domain"/> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">com.util.SQLServerDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.current_session_context_class">jta</prop> <prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory</prop> </props> </property> </bean> <bean id="hibernateTemplateFW" class="org.springframework.orm.hibernate4.HibernateTemplate" p:sessionFactory-ref="sessionFactoryFW" /> <bean class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" id="datasourceStk"> <property name="uniqueResourceName"><value>stk</value></property> <property name="xaDataSourceClassName" value="com.microsoft.sqlserver.jdbc.SQLServerXADataSource" /> <property name="xaProperties"> <props> <prop key="user">${jdbcstk.username}</prop> <prop key="password">${jdbcstk.password}</prop> <prop key="URL">${jdbcstk.url}</prop> </props> </property> </bean> <bean id="sessionFactoryStk" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="datasourceStk"/> <!-- --> <property name="packagesToScan" value="com.stk.domain"/> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">com.util.SQLServerDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.current_session_context_class">jta</prop> <prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory</prop> <!-- 4.0 <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop> <prop key="hibernate.cache.use_second_level_cache">true</prop>--> </props> </property> </bean> <bean id="hibernateTemplateStk" class="org.springframework.orm.hibernate4.HibernateTemplate" p:sessionFactory-ref="sessionFactoryStk" /> <!-- atomikos事务管理器 --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <description>UserTransactionManager</description> <property name="forceShutdown"> <value>true</value> </property> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300" /> </bean> <!-- spring 事务管理器 --> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager"/> <property name="userTransaction" ref="atomikosUserTransaction" /> <property name="allowCustomIsolationLevels" value="true"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="select*" read-only="true" propagation="REQUIRED"/> <tx:method name="get*" read-only="true" propagation="REQUIRED"/> <tx:method name="load*" read-only="true" propagation="REQUIRED"/> <tx:method name="find*" read-only="true" propagation="REQUIRED"/> <tx:method name="query*" read-only="true" propagation="REQUIRED"/> <tx:method name="read*" read-only="true" propagation="REQUIRED"/> <tx:method name="sync*"/> <tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="pointcut" expression="execution(* com.framework.service.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> </aop:config> </beans>
4.Spring DAO设计
采用SessionFactory注入的方式(不能采用HibernateTemplate)
BaseDao设计,session的管理,采用 ThreadLocal进行保存和获取,保证同一线程获取一次,
注意,数据操作之后要调用session.flush()进行保存。
/** * DAO基类,其它DAO可以直接继承这个DAO,不但可以复用共用的方法,还可以获得泛型的好处。 */ public abstract class BaseDao<T> { ThreadLocal<Session> localSession = new ThreadLocal<Session>(); private Class<T> entityClass; public abstract SessionFactory getSessionFactory(); /** * 通过反射获取子类确定的泛型类 */ public BaseDao() { Type genType = getClass().getGenericSuperclass(); Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); entityClass = (Class) params[0]; } /** * 根据ID加载PO实例 * * @param id * @return 返回相应的持久化PO实例 */ public T load(Serializable id) { return (T) getSession().load(entityClass, id); } /** * 根据ID获取PO实例 * * @param id * @return 返回相应的持久化PO实例 */ public T get(Serializable id) { return (T) getSession().get(entityClass, id); } /** * 保存PO * * @param entity */ public void save(T entity) { Session session = getSession(); session.save(entity); session.flush(); } public void saveOrUpdate(T entity) { Session session = getSession(); session.saveOrUpdate(entity); session.flush(); } /** * 删除PO * * @param entity */ public void remove(T entity) { Session session = getSession(); session.delete(entity); session.flush(); } /** * 更改PO * * @param entity */ public void update(T entity) { Session session = getSession(); session.update(entity); session.flush(); } public Query createSqlQuery(String sql, Object... values) { Assert.hasText(sql); Query query = getSession().createSQLQuery(sql); for (int i = 0; i < values.length; i++) { query.setParameter(i, values[i]); } return query; } public Session getSession() { //Session session =getSessionFactory().getCurrentSession(); //if (session == null) Session session = localSession.get(); if(session == null) { session = getSessionFactory().openSession(); localSession.set(session); } return session; } }