spring+springMVC+Mybatis架构下采用AbstractRoutingDataSource、atomikos、JTA实现多数据源灵活切换以及分布式事务管理
背景:
1、系统采用SSM架构、需要在10多个MYSQL数据库之间进行切换并对数据进行操作,上篇博文《springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法》
2、第一步先通过AbstractRoutingDataSource实现了多数据源的灵活切换,但是后来发现事务不起作用;
3、发现问题是因为重复扫描service包导致第二次扫入容器的BEAN没有纳入事务管理,因此在springMVC的配置文件中排除了对Service注解的扫描,防止重复扫描,事务生效了,测试又发现数据源不能成功切换了;
4、查阅资料发现是由于spring默认提供的事务管理DataSourceTransactionManager只能管理一个数据源的事务,因此考虑使用atomikos+JTA进行分布式事务管理
配置文件:
1、jdbc.properties
配置你需要连接的数据库资源
jdbc_zhs.driverClassName=com.mysql.jdbc.Driver
jdbc_zhs.url.spider=jdbc:mysql://127.0.0.1:3306/fms?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_zhs.username=root
jdbc_zhs.password=root jdbc_jcs.driverClassName=com.mysql.jdbc.Driver
jdbc_jcs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-jcs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_jcs.username=root
jdbc_jcs.password=root jdbc_hks.driverClassName=com.mysql.jdbc.Driver
jdbc_hks.url.spider=jdbc:mysql://127.0.0.1:3306/fms-hks?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_hks.username=root
jdbc_hks.password=root jdbc_xts.driverClassName=com.mysql.jdbc.Driver
jdbc_xts.url.spider=jdbc:mysql://127.0.0.1:3306/fms-xts?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_xts.username=root
jdbc_xts.password=root jdbc_xxgcs.driverClassName=com.mysql.jdbc.Driver
jdbc_xxgcs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-xxgcs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_xxgcs.username=root
jdbc_xxgcs.password=root jdbc_zdhs.driverClassName=com.mysql.jdbc.Driver
jdbc_zdhs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-zdhs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_zdhs.username=root
jdbc_zdhs.password=root jdbc_gfs.driverClassName=com.mysql.jdbc.Driver
jdbc_gfs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-gfs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_gfs.username=root
jdbc_gfs.password=root jdbc_kxs.driverClassName=com.mysql.jdbc.Driver
jdbc_kxs.url.spider=jdbc:mysql://127.0.0.1:3306/fms-kxs?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_kxs.username=root
jdbc_kxs.password=root jdbc_fyd.driverClassName=com.mysql.jdbc.Driver
jdbc_fyd.url.spider=jdbc:mysql://127.0.0.1:3306/fms-fyd?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_fyd.username=root
jdbc_fyd.password=root jdbc_ybj.driverClassName=com.mysql.jdbc.Driver
jdbc_ybj.url.spider=jdbc:mysql://127.0.0.1:3306/fms-ybj?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_ybj.username=root
jdbc_ybj.password=root jdbc_yzh.driverClassName=com.mysql.jdbc.Driver
jdbc_yzh.url.spider=jdbc:mysql://127.0.0.1:3306/fms-yzh?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_yzh.username=root
jdbc_yzh.password=root
2、applicationContext-common.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 启动扫描 -->
<context:component-scan base-package="com.fms;com.job;com.jmda;">
<!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> -->
</context:component-scan> <!-- 文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10240000"/>
<property name="maxInMemorySize" value="10240000" />
</bean> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPaths">
<list>
<value>/WEB-INF/pages/</value>
<value>/WEB-INF/template/</value>
<value>classpath:/jmda-ftl/</value>
</list>
</property>
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">0</prop>
<prop key="default_encoding">UTF-8</prop>
<prop key="number_format">0.##########</prop>
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
<prop key="classic_compatible">true</prop>
<prop key="template_exception_handler">ignore</prop>
</props>
</property>
</bean> <!-- 启用CGliB -->
<aop:aspectj-autoproxy /> <!-- 配置c3p0数据源,项目中有代码需要C3P0数据源支持时额外配置,不需要可以不配置 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="jdbcUrl">
<value><![CDATA[jdbc:mysql://localhost:3306/fms-zhs?useUnicode=yes&characterEncoding=UTF8]]></value>
</property>
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="user" value="root" />
<property name="password" value="root" />
<property name="maxPoolSize" value="200" />
<property name="minPoolSize" value="1" />
<property name="initialPoolSize" value="1" />
<property name="maxIdleTime" value="30" />
<property name="acquireIncrement" value="5" />
<property name="maxStatements" value="0" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="true" />
<property name="testConnectionOnCheckout" value="false" />
</bean> <!-- 注入数据源连接配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:/spring/jdbc.properties</value>
</list>
</property>
</bean> <!-- 多个数据源的公用配置,方便下面直接引用 -->
<bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
destroy-method="close" abstract="true">
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
<property name="poolSize" value="10" />
<property name="minPoolSize" value="10"/>
<property name="maxPoolSize" value="30"/>
<property name="borrowConnectionTimeout" value="60"/>
<property name="reapTimeout" value="20"/>
<!-- 最大空闲时间 -->
<property name="maxIdleTime" value="60"/>
<property name="maintenanceInterval" value="60" />
<property name="loginTimeout" value="60"/>
<property name="logWriter" value="60"/>
<property name="testQuery">
<value>select 1</value>
</property> </bean> <!-- 下面是所有需要切换的数据源 -->
<!-- 综合所数据源 -->
<bean id="ds_zhs" parent="abstractXADataSource">
<!-- value只要多个XA数据源之间不重复就行,随便取名 -->
<property name="uniqueResourceName" value="mysql/zhs" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_zhs.url.spider}</prop>
<prop key="user">${jdbc_zhs.username}</prop>
<prop key="password">${jdbc_zhs.password}</prop>
</props>
</property>
</bean> <!-- 舰船所数据源-->
<bean id="ds_jcs" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/jcs" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_jcs.url.spider}</prop>
<prop key="user">${jdbc_jcs.username}</prop>
<prop key="password">${jdbc_jcs.password}</prop>
</props>
</property>
</bean> <!-- 航空所数据源-->
<bean id="ds_hks" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/hks" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_hks.url.spider}</prop>
<prop key="user">${jdbc_hks.username}</prop>
<prop key="password">${jdbc_hks.password}</prop>
</props>
</property>
</bean> <!-- 系统所数据源-->
<bean id="ds_xts" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/xts" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_xts.url.spider}</prop>
<prop key="user">${jdbc_xts.username}</prop>
<prop key="password">${jdbc_xts.password}</prop>
</props>
</property>
</bean> <!-- 信息工程所数据源-->
<bean id="ds_xxgcs" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/xxgcs" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_xxgcs.url.spider}</prop>
<prop key="user">${jdbc_xxgcs.username}</prop>
<prop key="password">${jdbc_xxgcs.password}</prop>
</props>
</property>
</bean> <!-- 自动化所数据源-->
<bean id="ds_zdhs" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/zdhs" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_zdhs.url.spider}</prop>
<prop key="user">${jdbc_zdhs.username}</prop>
<prop key="password">${jdbc_zdhs.password}</prop>
</props>
</property>
</bean> <!-- 规范所数据源-->
<bean id="ds_gfs" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/gfs" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_gfs.url.spider}</prop>
<prop key="user">${jdbc_gfs.username}</prop>
<prop key="password">${jdbc_gfs.password}</prop>
</props>
</property>
</bean> <!-- 科信所数据源-->
<bean id="ds_kxs" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/kxs" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_kxs.url.spider}</prop>
<prop key="user">${jdbc_kxs.username}</prop>
<prop key="password">${jdbc_kxs.password}</prop>
</props>
</property>
</bean> <!-- 翻译队数据源-->
<bean id="ds_fyd" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/fyd" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_fyd.url.spider}</prop>
<prop key="user">${jdbc_fyd.username}</prop>
<prop key="password">${jdbc_fyd.password}</prop>
</props>
</property>
</bean> <!-- 院本级数据源-->
<bean id="ds_ybj" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/ybj" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_ybj.url.spider}</prop>
<prop key="user">${jdbc_ybj.username}</prop>
<prop key="password">${jdbc_ybj.password}</prop>
</props>
</property>
</bean> <!-- 院综合系统数据源-->
<bean id="ds_yzh" parent="abstractXADataSource">
<property name="uniqueResourceName" value="mysql/yzh" />
<property name="xaDataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">${jdbc_yzh.url.spider}</prop>
<prop key="user">${jdbc_yzh.username}</prop>
<prop key="password">${jdbc_yzh.password}</prop>
</props>
</property>
</bean> <!-- 配置多数据源 MultipleDataSource-->
<bean name="dynamicDatasource" class="com.fms.common.datasource.MultipleDataSource">
<property name="targetDataSources">
<map>
<!-- key和value-ref尽量保持一致,我在测试的时候因为名称不一致一致报错,
找了好久都没找到原因,最后统一了名称终于成功启动了 -->
<entry key="ds_zhs" value-ref="ds_zhs"/>
<entry key="ds_jcs" value-ref="ds_jcs"/>
<entry key="ds_hks" value-ref="ds_hks"/>
<entry key="ds_xts" value-ref="ds_xts"/>
<entry key="ds_xxgcs" value-ref="ds_xxgcs"/>
<entry key="ds_zdhs" value-ref="ds_zdhs"/>
<entry key="ds_gfs" value-ref="ds_gfs"/>
<entry key="ds_kxs" value-ref="ds_kxs"/>
<entry key="ds_fyd" value-ref="ds_fyd"/>
<entry key="ds_ybj" value-ref="ds_ybj"/>
<entry key="ds_yzh" value-ref="ds_yzh"/>
</map>
</property>
<!-- 指定一个默认的数据源,即在不需要切换数据源时,本地系统默认使用的数据源 -->
<property name="defaultTargetDataSource" ref="ds_zhs" />
</bean> <!-- 下面开始配置SqlSessionFactoryBean,有多少个数据源需要支持就陪多少个 -->
<bean id="sqlSessionFactory_zhs" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- dataSource的ref对应数据源的id -->
<property name="dataSource" ref="ds_zhs"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_jcs" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_jcs"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_hks" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_hks"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_xts" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_xts"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_xxgcs" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_xxgcs"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_zdhs" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_zdhs"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_gfs" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_gfs"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_kxs" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_kxs"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_fyd" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_fyd"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_ybj" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_ybj"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory_yzh" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_yzh"/>
<property name="configLocation" value="classpath:mybatis.xml" />
<property name="mapperLocations">
<list>
<value>classpath*:/com/fms/**/dao/*Mapper.xml</value>
<value>classpath*:/com/fms/**/dao/*DAO.xml</value>
</list>
</property>
</bean> <!-- 配置自定义的SqlSessionTemplate模板,注入相关配置 -->
<bean id="sqlSessionTemplate" class="com.fms.common.datasource.CustomSqlSessionTemplate" scope="prototype">
<!-- 构造注入参数指定本地默认数据源对应的SqlSessionFactoryBean -->
<constructor-arg ref="sqlSessionFactory_zhs" />
<property name="targetSqlSessionFactorys">
<map>
<!-- key和上文配置的数据源的id值尽量保持一致,我在测试的时候因为名称不一致一致报错,
找了好久都没找到原因,最后统一了名称终于成功启动了 -->
<entry value-ref="sqlSessionFactory_zhs" key="ds_zhs"/>
<entry value-ref="sqlSessionFactory_jcs" key="ds_jcs"/>
<entry value-ref="sqlSessionFactory_hks" key="ds_hks"/>
<entry value-ref="sqlSessionFactory_xts" key="ds_xts"/>
<entry value-ref="sqlSessionFactory_xxgcs" key="ds_xxgcs"/>
<entry value-ref="sqlSessionFactory_zdhs" key="ds_zdhs"/>
<entry value-ref="sqlSessionFactory_gfs" key="ds_gfs"/>
<entry value-ref="sqlSessionFactory_kxs" key="ds_kxs"/>
<entry value-ref="sqlSessionFactory_fyd" key="ds_fyd"/>
<entry value-ref="sqlSessionFactory_ybj" key="ds_ybj"/>
<entry value-ref="sqlSessionFactory_yzh" key="ds_yzh"/>
</map>
</property>
</bean> <!-- 配置mybatis接口扫描 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.fms.**.dao" />
<property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
</bean> <!-- jta配置,直接复用,不需要修改 -->
<!-- jta配置开始 -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown">
<value>true</value>
</property>
</bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean> <bean id="springTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<ref bean="atomikosTransactionManager" />
</property>
<property name="userTransaction">
<ref bean="atomikosUserTransaction" />
</property>
</bean>
<!-- jta配置结束 --> <!-- 配置事务管理 -->
<tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" /> </beans>
3、springmvc-servlet.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:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.1.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd"> <context:component-scan base-package="com.fms;com.jmda;com.job;" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
<!-- <context:annotation-config/> --> <!-- message-converters -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven> <!-- 视图解析器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="suffix" value=".ftl" />
<property name="contentType" value="text/html;charset=UTF-8"></property>
<property name="requestContextAttribute" value="request" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="exposeRequestAttributes" value="true" />
<property name="exposeSessionAttributes" value="true" />
</bean> <!-- 静态资源 -->
<mvc:resources mapping="/static/**" location="/static/" />
<mvc:resources mapping="/jmda-static/**" location="/jmda-static/" />
<mvc:resources mapping="/assets/**" location="/assets/" /> <!-- 拦截器 -->
<mvc:interceptors>
<bean class="com.fms.common.listener.SecurityInterceptor"/>
</mvc:interceptors>
</beans>
4、web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/spring/applicationContext*.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter> <filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <servlet>
<servlet-name>spring4mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring/springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring4mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--文件下载 -->
<servlet>
<servlet-name>ServletDownload</servlet-name>
<servlet-class>com.fms.business.sjtb.service.ServletDownload</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDownload</servlet-name>
<url-pattern>/servlet/download</url-pattern>
</servlet-mapping>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>com.fms.common.listener.CommListener</listener-class>
</listener>
<!-- 400错误 -->
<error-page>
<error-code>400</error-code>
<location>/error</location>
</error-page>
<!-- 404 页面不存在错误 -->
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
<!-- 403 服务器拒绝请求 -->
<error-page>
<error-code>403</error-code>
<location>/error</location>
</error-page>
<!-- 500 服务器内部错误 -->
<error-page>
<error-code>500</error-code>
<location>/error</location>
</error-page>
<!-- 503 服务不可用 -->
<error-page>
<error-code>503</error-code>
<location>/error</location>
</error-page>
<!-- java.lang.Exception -->
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error</location>
</error-page>
<!-- java.lang.NullPointerException -->
<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/error</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/error</location>
</error-page>
<welcome-file-list>
<welcome-file></welcome-file>
</welcome-file-list>
</web-app>
5、在resources文件夹下增加jta.properties文件:
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name = tm.out
com.atomikos.icatch.log_base_name = tmlog
com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm
com.atomikos.icatch.console_log_level=WARN
JAVA代码:
1.MultipleDataSource:
package com.fms.common.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class MultipleDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>(); //将数据源重置为默认数据源
public static void changeTodefaultDataSource() {
dataSourceKey.remove();
}
// //院
// public static void setDataSourceY(){
// dataSourceKey.remove();
// dataSourceKey.set("yzhDataSource");
// } //将数据源设置为配置文件中key值为dataSource参数对应的值的数据源
public static void setDataSource(String dataSource){
dataSourceKey.remove();
dataSourceKey.set(dataSource);
} //获取当前数据源的key值
public static String getKey(){
return dataSourceKey.get();
} //重写AbstractRoutingDataSource的方法,提供当前的数据源用于连接
@Override
protected Object determineCurrentLookupKey() {
return dataSourceKey.get();
} }
2、自定义的SqlSessionTemlate类:
CustomSqlSessionTemplate
package com.fms.common.datasource; import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map; import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert; public class CustomSqlSessionTemplate extends SqlSessionTemplate { private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator; private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
private SqlSessionFactory defaultTargetSqlSessionFactory; public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
this.targetSqlSessionFactorys = targetSqlSessionFactorys;
} public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
} public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
} public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
.getEnvironment().getDataSource(), true));
} public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) { super(sqlSessionFactory, executorType, exceptionTranslator); this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor()); this.defaultTargetSqlSessionFactory = sqlSessionFactory;
} @Override
public SqlSessionFactory getSqlSessionFactory() { SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(MultipleDataSource.getKey());
if (targetSqlSessionFactory != null) {
return targetSqlSessionFactory;
} else if (defaultTargetSqlSessionFactory != null) {
return defaultTargetSqlSessionFactory;
} else {
Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
}
return this.sqlSessionFactory;
} @Override
public Configuration getConfiguration() {
return this.getSqlSessionFactory().getConfiguration();
} public ExecutorType getExecutorType() {
return this.executorType;
} public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
return this.exceptionTranslator;
} /**
* {@inheritDoc}
*/
public <T> T selectOne(String statement) {
return this.sqlSessionProxy.<T> selectOne(statement);
} /**
* {@inheritDoc}
*/
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.<T> selectOne(statement, parameter);
} /**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
} /**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
} /**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
} /**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.<E> selectList(statement);
} /**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.<E> selectList(statement, parameter);
} /**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
} /**
* {@inheritDoc}
*/
public void select(String statement, ResultHandler handler) {
this.sqlSessionProxy.select(statement, handler);
} /**
* {@inheritDoc}
*/
public void select(String statement, Object parameter, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, handler);
} /**
* {@inheritDoc}
*/
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
} /**
* {@inheritDoc}
*/
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
} /**
* {@inheritDoc}
*/
public int insert(String statement, Object parameter) {
return this.sqlSessionProxy.insert(statement, parameter);
} /**
* {@inheritDoc}
*/
public int update(String statement) {
return this.sqlSessionProxy.update(statement);
} /**
* {@inheritDoc}
*/
public int update(String statement, Object parameter) {
return this.sqlSessionProxy.update(statement, parameter);
} /**
* {@inheritDoc}
*/
public int delete(String statement) {
return this.sqlSessionProxy.delete(statement);
} /**
* {@inheritDoc}
*/
public int delete(String statement, Object parameter) {
return this.sqlSessionProxy.delete(statement, parameter);
} /**
* {@inheritDoc}
*/
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
} /**
* {@inheritDoc}
*/
public void commit() {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void commit(boolean force) {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void rollback() {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void rollback(boolean force) {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void close() {
throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void clearCache() {
this.sqlSessionProxy.clearCache();
} /**
* {@inheritDoc}
*/
public Connection getConnection() {
return this.sqlSessionProxy.getConnection();
} /**
* {@inheritDoc}
* @since 1.0.2
*/
public List<BatchResult> flushStatements() {
return this.sqlSessionProxy.flushStatements();
} /**
* Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
* unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
* the {@code PersistenceExceptionTranslator}.
*/
private class SqlSessionInterceptor implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = getSqlSession(
CustomSqlSessionTemplate.this.getSqlSessionFactory(),
CustomSqlSessionTemplate.this.executorType,
CustomSqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory());
}
}
} }
3、数据源切换切面ChooseDataSourceAspect:
package com.fms.common.aop; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component; import com.fms.common.annotation.ChooseDataSource;
import com.fms.common.datasource.MultipleDataSource;
import com.fms.common.utils.reflect.ReflectUtil; /**
* 类描述:完成数据源的切换,抽类切面,具体项目继承一下,不需要重写即可使用
*/
@Aspect
public abstract class ChooseDataSourceAspect { protected static final ThreadLocal<String> preDatasourceHolder = new ThreadLocal<String>(); @Pointcut("@annotation(com.fms.common.annotation.ChooseDataSource)")
public void methodWithChooseAnnotation() {
System.err.println("************************************************//////////////////////////////////////////");
} /**
* 根据@ChooseDataSource的属性值设置不同的dataSourceKey,以供DynamicDataSource
*/
@Before("methodWithChooseAnnotation()")
public void changeDataSourceBeforeMethodExecution(JoinPoint jp) {
//拿到anotation中配置的数据源
String resultDS = determineDatasource(jp);
//没有配置实用默认数据源
if (resultDS == null) {
MultipleDataSource.changeTodefaultDataSource();
return;
}
preDatasourceHolder.set(MultipleDataSource.getKey());
//将数据源设置到数据源持有者
MultipleDataSource.setDataSource(resultDS); } /**
* 如果需要修改获取数据源的逻辑,请重写此方法 *
* @param jp
* @return
*/
@SuppressWarnings("rawtypes")
protected String determineDatasource(JoinPoint jp) {
String methodName = jp.getSignature().getName();
Class targetClass = jp.getSignature().getDeclaringType();
String dataSourceForTargetClass = resolveDataSourceFromClass(targetClass);
String dataSourceForTargetMethod = resolveDataSourceFromMethod(targetClass, methodName);
String resultDS = determinateDataSource(dataSourceForTargetClass, dataSourceForTargetMethod);
return resultDS;
} /**
* 方法执行完毕以后,数据源切换回之前的数据源。
* 比如foo()方法里面调用bar(),但是bar()另外一个数据源,
* bar()执行时,切换到自己数据源,执行完以后,要切换到foo()所需要的数据源,以供
* foo()继续执行。
*/
@After("methodWithChooseAnnotation()")
public void restoreDataSourceAfterMethodExecution() {
MultipleDataSource.setDataSource(preDatasourceHolder.get());
preDatasourceHolder.remove();
} /**
* @param targetClass
* @param methodName
* @return
*/
@SuppressWarnings("rawtypes")
private String resolveDataSourceFromMethod(Class targetClass, String methodName) { Method m = ReflectUtil.findUniqueMethod(targetClass, methodName);
if (m != null) {
ChooseDataSource choDs = m.getAnnotation(ChooseDataSource.class);
return resolveDataSourcename(choDs);
}
return null;
} /**
* 方法描述 :
* 确定最终数据源,如果方法上设置有数据源,则以方法上的为准,如果方法上没有设置,则以类上的为准,如果类上没有设置,则使用默认数据源
*
* @param classDS
* @param methodDS
* @return
*/
private String determinateDataSource(String classDS, String methodDS) {
// if (null == classDS && null == methodDS) {
// return null;
// }
// 两者必有一个不为null,如果两者都为Null,也会返回Null
return methodDS == null ? classDS : methodDS;
} /**
* 方法描述 : 类级别的 @ChooseDataSource的解析
*
* @param targetClass
* @return
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private String resolveDataSourceFromClass(Class targetClass) {
ChooseDataSource classAnnotation = (ChooseDataSource) targetClass
.getAnnotation(ChooseDataSource.class);
// 直接为整个类进行设置
return null != classAnnotation ? resolveDataSourcename(classAnnotation)
: null;
} /**
* 方法描述 :
* 组装DataSource的名字
*
* @param ds
* @return
*/
private String resolveDataSourcename(ChooseDataSource ds) {
return ds == null ? null : ds.value();
} }
4、数据源切换注解ChooseDataSource:
package com.fms.common.annotation; import java.lang.annotation.*; /**
* 注解式数据源,用来进行数据源切换
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChooseDataSource { String value() default "";
}
5、使用方式:
TestService:
package com.fms.business; import java.util.Map; import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.fms.common.dao.table.mapper.WfWorkflowPOMapper;
import com.fms.common.datasource.MultipleDataSource;
import com.fms.common.model.po.WfWorkflowPO;
import com.google.common.collect.Maps; @Service(value="testService")
public class TestService { //注入SqlSessionTemplate,执行自定义的Mapper.xml文件
@Autowired
SqlSessionTemplate sqlSessionTemplate;
String nameSpace = "com.fms.business.dao.TestServiceMapper"; @Transactional
public void test() throws Exception{
Map<String, Object> param = Maps.newHashMap();
param.put("id", "123");
param.put("name", "zhangsan");
//默认数据源ds_zhs
sqlSessionTemplate.insert(nameSpace+".testInsert", param);
//切换至ds_jcs
MultipleDataSource.setDataSource("ds_jcs");
sqlSessionTemplate.insert(nameSpace+".testInsert", param);
} @Transactional
public void test1() throws Exception{
Map<String, Object> param = Maps.newHashMap();
param.put("id", "124");
param.put("name", "lisi");
//默认数据源ds_zhs
sqlSessionTemplate.insert(nameSpace+".testInsert", param);
//切换至ds_jcs
MultipleDataSource.setDataSource("ds_jcs");
sqlSessionTemplate.insert(nameSpace+".testInsert", param);
//编写抛出异常的代码测试事务回滚
String str = null;
str.trim();
} //使用MyBatis自动生成的Mapper接口
@Autowired
WfWorkflowPOMapper wfWorkflowPOMapper;
@Transactional
public void test2() throws Exception{
WfWorkflowPO record = new WfWorkflowPO();
record.setWorkflowId("12345");
record.setName("xxxx");
wfWorkflowPOMapper.insert(record);
//切换至ds_jcs
MultipleDataSource.setDataSource("ds_jcs");
wfWorkflowPOMapper.insert(record);
//切换至ds_yzh
MultipleDataSource.setDataSource("ds_yzh");
wfWorkflowPOMapper.insert(record);
} }
TestController:
package com.fms.business; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; @Controller
public class TestController { @Autowired
TestService testService; @RequestMapping("/test")
@ResponseBody
public String test(){
try {
testService.test();
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
} @RequestMapping("/test1")
@ResponseBody
public String test1(){
try {
testService.test1();
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
} @RequestMapping("/test2")
@ResponseBody
public String test2(){
try {
testService.test2();
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
}
}Aspect
原例中还有使用@ChooseDataSource注解切换数据源的用法,需要AspectJ编译,捣鼓了一下没成功,数据源能够切换,但是分布式事务不起作用,有兴趣可以自己试试自己调试修改。
在项目中加入一个继承ChooseDataSourceAspect的类即可:
package com.fms.business;ChooseDataSource import org.aspectj.lang.annotation.Aspect; import com.fms.common.aop.ChooseDataSourceAspect;
@Component
@Aspect
public class TestAspect extends ChooseDataSourceAspect { }
参考博文:http://www.blogjava.net/zuxiong/archive/2015/09/24/427471.html
spring+springMVC+Mybatis架构下采用AbstractRoutingDataSource、atomikos、JTA实现多数据源灵活切换以及分布式事务管理的更多相关文章
- Spring+SpringMVC+MyBatis深入学习及搭建(十一)——SpringMVC架构
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6985816.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十)--My ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(九)Linux下安装redis及redis的常用命令和操作
redis简介 Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis与其他key-value缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存 ...
- Java架构学习 转(Spring+SpringMVC+MyBatis+easyUI)
Spring+SpringMVC+MyBatis+easyUI : http://www.cnblogs.com/han-1034683568/p/6730869.html
- 基于Spring+SpringMVC+Mybatis的Web系统搭建
系统搭建的配置大同小异,本文在前人的基础上做了些许的改动,重写数据库,增加依据权限的动态菜单的实现,也增加了后台返回json格式数据的配置,详细参见完整源码. 主要的后端架构:Spring+Sprin ...
- Spring+SpringMVC+MyBatis深入学习及搭建(十六)——SpringMVC注解开发(高级篇)
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7085268.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十五)——S ...
- 用 eclipse 创建一个简单的 meaven spring springMvc mybatis 项目
下面是整体步骤: 1: 先创建一个Maven 项目: 选择跳过骨架: 因为要搭建的是 web 项目 所以这个地方选择 war 包; 点击完成 这样就完成 Maven项目的搭建: 接下俩 先把 Mav ...
- SSM(Spring+SpringMVC+Mybatis)框架环境搭建(整合步骤)(一)
1. 前言 最近在写毕设过程中,重新梳理了一遍SSM框架,特此记录一下. 附上源码:https://gitee.com/niceyoo/jeenotes-ssm 2. 概述 在写代码之前我们先了解一下 ...
- 如约而至,Java 10 正式发布! Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势 努力的孩子运气不会太差,跌宕的人生定当更加精彩 优先队列详解(转载)
如约而至,Java 10 正式发布! 3 月 20 日,Oracle 宣布 Java 10 正式发布. 官方已提供下载:http://www.oracle.com/technetwork/java ...
- java实现微信支付宝等多个支付平台合一的二维码支付(maven+spring springmvc mybatis框架)
首先申明,本人实现微信支付宝等支付平台合多为一的二维码支付,并且实现有效时间内支付有效,本人采用的框架是spring springmvc mybatis 框架,maven管理.其实如果支付,不需要my ...
随机推荐
- Luogu P3459 [POI2007]MEG-Megalopolis(线段树)
P3459 [POI2007]MEG-Megalopolis 题意 题目描述 Byteotia has been eventually touched by globalisation, and so ...
- [Array]414. Third Maximum Number
Given a non-empty array of integers, return the third maximum number in this array. If it does not e ...
- 【python之路36】进程、线程、协程相关
线程详细用法请参考:http://www.cnblogs.com/sunshuhai/articles/6618894.html 一.初始多线程 通过下面两个例子的运行效率,可以得知多线程的速度比单线 ...
- HDU6200 mustedge mustedge mustedge
不用看题就知道这是和什么tarjan.缩点或桥一类有关的题. 谁让他取题目叫一个mustedge还连续写3次的(哦,似乎是因为那个比赛的题目都是这个画风) 必须的边 >必须要经过的边 > ...
- textarea限定字符输入及提示
html <textarea type="text" name="goodsDesc" data-varify='goods' placeholder=& ...
- Dockerfile Tomcat镜像制作
FROM centos MAINTAINER taohaijun "thjtao@126.com" WORKDIR /home #上传安装包 COPY jdk-8u131-linu ...
- 使用JSONObject进行序列化时,避开定义get或set为开头的方法名称
从结果中可以看到,JSONObject对Test对象进行序列化时,把fileName也当做属性了. 原因:涉及到JavaBean规范(参考:https://www.cnblogs.com/yusimi ...
- wamp httpd-vhosts.conf
配置Apache的httpd.conf文件 Include conf/extra/httpd-vhosts.conf 修改apache的vhost文件 <VirtualHost *:> D ...
- jquery源码学习(二)——jquery中的变量
jquery在 21-93 行提供了变量 var // A central reference to the root jQuery(document) rootjQuery, // The defe ...
- 一位AI研究员+区块链创业者的终极展望:AI DAO将统治世界
一位AI研究员+区块链创业者的终极展望:AI DAO将统治世界 [日期:2017-01-09] 来源:infoq.com 作者:杨赛 [字体:大 中 小] Trent McConaghy是一位资深的 ...