---恢復內容開始---
在做項目過程中,遇到了需要一個項目中訪問兩個數據庫的情況,發現使用常規的spring管理事務,導致事務不能正常回滾,因此,采用了jta+atomikos的分布式數據源方式對事務進行管理。在此做下記錄,以便日后參考,文中用詞不當之處,敬請諒解!
配置文件:
1:web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <!-- 加載Spring配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring.xml </param-value> </context-param> <!-- 字符集 過濾器 --> <filter> <filter-name>encodingFilter</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>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Spring監聽器 --> <listener> <description>Spring監聽器</description> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 防止Spring內存溢出監聽器 --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <!-- Spring MVC --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <description>SpringMVC</description> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- Session超時時間 --> <session-config> <session-timeout>15</session-timeout> </session-config> </web-app>
2:pom.xml
<properties> <project.build.sourceEncoding>UTF- 8</project.build.sourceEncoding> <spring.version>4.2.5.RELEASE</spring.version> <junit.version>4.12</junit.version> <druid.version>1.0.18</druid.version> <fastjson.version>1.2.6</fastjson.version> <mybaitsplus.version>2.0.8</mybaitsplus.version> <mysql.version>5.1.38</mysql.version> <log4j.version>1.2.17</log4j.version> <slf4j.version>1.7.13</slf4j.version> <aspectjweaver.version>1.8.9</aspectjweaver.version> <fileupload.version>1.3.1</fileupload.version> <commons.version>2.4</commons.version> <jstl.version>1.2</jstl.version> </properties> <dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- spring quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <!-- 添加Hibernate依賴 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>3.6.10.Final</version> </dependency> <!-- redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.5.1</version> </dependency> <!-- json --> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-asl</artifactId> <version>1.9.11</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.11</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.2.2</version> </dependency> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.6.2.RELEASE</version> </dependency> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> <type>jar</type> <scope>compile</scope> </dependency> <!-- AOP --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectjweaver.version}</version> </dependency> <!-- spring mvc upload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${fileupload.version}</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <!-- Mybatis-Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>${mybaitsplus.version}</version> </dependency> <!-- httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3</version> </dependency> <dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.3</version> </dependency> <!-- httpclient end --> <!-- oracle --> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.1.0</version> </dependency> <!-- Druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!-- FastJson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!-- Log --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- 新增 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.7.4</version> </dependency> <!-- 校驗 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.1.3.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.1.1.GA</version> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.2</version> <exclusions> <exclusion> <artifactId>quartz</artifactId> <groupId>org.opensymphony.quartz</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.2</version> </dependency> <!-- mapper生成需要依賴 --> <!-- 模板引擎 --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency> <!-- FastdfsClient --> <dependency> <groupId>org.csource</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27-SNAPSHOT</version> </dependency> <!-- atomikos 多數據源事務管理 --> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-util</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-api</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc-deprecated</artifactId> <version>3.7.0</version> </dependency> </dependencies> <build> <finalName>dataPlat</finalName> </build>
3:config.properties
配置數據源屬性
A_jdbc_url=jdbc\:oracle\:thin\:@localhost\:1521\:orcl A_jdbc_username=test_one A_jdbc_password=password B_jdbc_url=jdbc\:oracle\:thin\:@localhost\:1521\:orcl B_jdbc_username=test_two B_jdbc_password=password
4、在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=INFO
5、spring-mvc.xml
<mvc:default-servlet-handler/> <!-- Controller包(自動注入) --> <context:component-scan base-package="com.zzxw.web.controller"> </context:component-scan> <mvc:annotation-driven> <!-- 處理responseBody 里面日期類型 --> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.fasterxml.jackson.databind.ObjectMapper"> <property name="dateFormat"> <bean class="java.text.SimpleDateFormat"> <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" /> </bean> </property> </bean> </property> </bean> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" > <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 靜態資源配置 --> <mvc:resources mapping="/resources/**" location="/resources/"/> <!-- 對模型視圖名稱的解析,即在模型視圖名稱添加前后綴 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 上傳限制 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 上傳文件大小限制為31M,31*1024*1024 --> <property name="maxUploadSize" value="32505856"/> </bean> <bean id="actionLogAdvice" class="com.zzxw.web.annotation.ActionLogAdvice" /> <aop:config> <aop:pointcut id="actionLog" expression="execution(public * com.zzxw.web.controller..*..*(..)) " /> <aop:advisor pointcut-ref="actionLog" advice-ref="actionLogAdvice"/> </aop:config> <!-- 總攔截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <mvc:exclude-mapping path="/js/**" /> <mvc:exclude-mapping path="/css/**" /> <mvc:exclude-mapping path="/images/**" /> <bean class="com.zzxw.web.interceptor.SpringRequestInterceptor" /> </mvc:interceptor> </mvc:interceptors>
6:spring-mybatis.xml
<!-- 啟動AspectJ支持 <aop:aspectj-autoproxy />--> <context:property-placeholder location="classpath:config.properties" /> <!-- 啟用CGliB --> <!-- <aop:aspectj-autoproxy /> --> <!-- 多個數據源的公用配置,方便下面直接引用 --> <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true"> <property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource"/> <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 * from dual</value> </property> </bean>
<!--數據源A--> <bean id="dataSourceA" parent="abstractXADataSource"> <!-- value只要多個XA數據源之間不重復就行,隨便取名 --> <property name="uniqueResourceName" value="dataPlat" /> <property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource" /> <property name="xaProperties"> <props> <prop key="URL">${A_jdbc_url}</prop> <prop key="user">${A_jdbc_username}</prop> <prop key="password">${A_jdbc_password}</prop> </props> </property> </bean> <!--數據源B--> <bean id="dataSourceB" parent="abstractXADataSource"> <property name="uniqueResourceName" value="risk" /> <property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource" /> <property name="xaProperties"> <props> <prop key="URL">${B_jdbc_url}</prop> <prop key="user">${B_jdbc_username}</prop> <prop key="password">${B_jdbc_password}</prop> </props> </property> </bean> <!-- Spring整合Mybatis,更多查看文檔:http://mp.baomidou.com --> <bean id="sqlSessionFactory_A" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceA"/> <!-- 自動掃描Mapping.xml文件 --> <property name="mapperLocations" value="classpath:mybatis/dataplat/*.xml"/> <property name="configLocation" value="classpath:spring/mybatis-config.xml"/> <property name="typeAliasesPackage" value="com.zzxw.web.entity"/> <property name="plugins"> <array> <!-- 分頁插件配置 --> <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"> <property name="dialectType" value="oracle"/> </bean> </array> </property> <!-- 全局配置注入 --> <property name="globalConfig" ref="globalConfig" /> </bean> <!-- Spring整合Mybatis,更多查看文檔:http://mp.baomidou.com --> <bean id="sqlSessionFactory_B" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceB"/> <!-- 自動掃描Mapping.xml文件 --> <property name="mapperLocations" value="classpath:mybatis/risk/*.xml"/> <property name="configLocation" value="classpath:spring/mybatis-config.xml"/>
<!--對應的實體類--> <property name="typeAliasesPackage" value="com.zzxw.web.domain"/> <property name="plugins"> <array> <!-- 分頁插件配置 --> <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"> <property name="dialectType" value="oracle"/> </bean> </array> </property> <!-- 全局配置注入 --> <property name="globalConfig" ref="globalConfig" /> </bean> <!-- 配置多數據源 MultipleDataSource--> <bean name="dynamicDatasource" class="com.zzxw.web.common.DynamicDataSource"> <property name="targetDataSources"> <map> <!-- key和value-ref盡量保持一致,我在測試的時候因為名稱不一致一致報錯, 找了好久都沒找到原因,最后統一了名稱終於成功啟動了 --> <entry key="dataSourceA" value-ref="dataSourceA"></entry> <entry key="dataSourceB" value-ref="dataSourceB"></entry> </map> </property> <!-- 指定一個默認的數據源,即在不需要切換數據源時,本地系統默認使用的數據源 --> <property name="defaultTargetDataSource" ref="dataSourceA" /> </bean> <bean id="dataSourceAspect" class="com.zzxw.web.common.DataSourceAspect"/> <aop:config> <aop:aspect ref="dataSourceAspect"> <aop:pointcut id="dataSourcePointcut" expression="execution(* com.zzxw.web.*.mapper.*.*(..))"/> <aop:before pointcut-ref="dataSourcePointcut" method="intercept" /> </aop:aspect> </aop:config> <!-- 配置自定義的SqlSessionTemplate模板,注入相關配置 --> <bean id="sqlSessionTemplate" class="com.zzxw.web.common.CustomSqlSessionTemplate" scope="prototype"> <!-- 構造注入參數指定本地默認數據源對應的SqlSessionFactoryBean --> <constructor-arg ref="sqlSessionFactory_A" /> <property name="targetSqlSessionFactorys"> <map> <!-- key和上文配置的數據源的id值盡量保持一致,我在測試的時候因為名稱不一致一致報錯, 找了好久都沒找到原因,最后統一了名稱終於成功啟動了 --> <entry value-ref="sqlSessionFactory_A" key="dataSourceA"/> <entry value-ref="sqlSessionFactory_B" key="dataSourceB"/> </map> </property> </bean> <!-- MyBatis 動態掃描 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.zzxw.web.**.mapper"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> <!-- 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" /> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dynamicDatasource"/> <bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration"> <!-- AUTO->`0`("數據庫ID自增") INPUT->`1`(用戶輸入ID") ID_WORKER->`2`("全局唯一ID") UUID->`3`("全局唯一ID") --> <property name="idType" value="3" /> <!-- MYSQL->`mysql` ORACLE->`oracle` DB2->`db2` H2->`h2` HSQL->`hsql` SQLITE->`sqlite` POSTGRE->`postgresql` SQLSERVER2005->`sqlserver2005` SQLSERVER->`sqlserver` --> <!-- Oracle需要添加該項 --> <property name="dbType" value="oracle" /> <!-- 全局表為下划線命名設置 true --> <property name="dbColumnUnderline" value="true" /> </bean>
7:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- | plugins在配置文件中的位置必須符合要求,否則會報錯,順序如下: | properties?, settings?, | typeAliases?, typeHandlers?, | objectFactory?,objectWrapperFactory?, | plugins?, | environments?, databaseIdProvider?, mappers? |--> <configuration> <!-- | 全局配置設置 | | 可配置選項 默認值, 描述 | | aggressiveLazyLoading true, 當設置為‘true’的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載。 | multipleResultSetsEnabled true, 允許和不允許單條語句返回多個數據集(取決於驅動需求) | useColumnLabel true, 使用列標簽代替列名稱。不同的驅動器有不同的作法。參考一下驅動器文檔,或者用這兩個不同的選項進行測試一下。 | useGeneratedKeys false, 允許JDBC 生成主鍵。需要驅動器支持。如果設為了true,這個設置將強制使用被生成的主鍵,有一些驅動器不兼容不過仍然可以執行。 | autoMappingBehavior PARTIAL, 指定MyBatis 是否並且如何來自動映射數據表字段與對象的屬性。PARTIAL將只自動映射簡單的,沒有嵌套的結果。FULL 將自動映射所有復雜的結果。 | defaultExecutorType SIMPLE, 配置和設定執行器,SIMPLE 執行器執行其它語句。REUSE 執行器可能重復使用prepared statements 語句,BATCH執行器可以重復執行語句和批量更新。 | defaultStatementTimeout null, 設置一個時限,以決定讓驅動器等待數據庫回應的多長時間為超時 | --> <settings> <!-- 這個配置使全局的映射器啟用或禁用緩存 --> <setting name="cacheEnabled" value="true"/> <!-- 全局啟用或禁用延遲加載。當禁用時,所有關聯對象都會即時加載 --> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="defaultExecutorType" value="REUSE"/> <setting name="defaultStatementTimeout" value="25000"/> <setting name="callSettersOnNulls" value="true"/> </settings> </configuration>
8:spring.xml
<!-- 引入屬性文件 --> <context:property-placeholder location="classpath:config.properties"/> <!-- Service包(自動注入) --> <context:component-scan base-package="com.zzxw.web.service"/> <import resource="classpath:spring/spring-mybatis.xml"/>
9.CustomSqlSessionTemplate.java
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(DynamicDataSourceHolder.getDataSource()); 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()); } } } }
多數據源切換代碼
1、DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // 從自定義的位置獲取數據源標識 return DynamicDataSourceHolder.getDataSource(); } }
2、DataSourceAspect.java
public class DataSourceAspect { /** * 攔截目標方法,獲取由@DataSource指定的數據源標識,設置到線程存儲中以便切換數據源 * * @param point * @throws Exception */ public void intercept(JoinPoint point) throws Exception { Class<?> target = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); // 默認使用目標類型的注解,如果沒有則使用其實現接口的注解 for (Class<?> clazz : target.getInterfaces()) { resolveDataSource(clazz, signature.getMethod()); } resolveDataSource(target, signature.getMethod()); } /** * 提取目標對象方法注解和類型注解中的數據源標識 * * @param clazz * @param method */ private void resolveDataSource(Class<?> clazz, Method method) { try { Class<?>[] types = method.getParameterTypes(); // 默認使用類型注解 if (clazz.isAnnotationPresent(DataSource.class)) { DataSource source = clazz.getAnnotation(DataSource.class); DynamicDataSourceHolder.setDataSource(source.value()); } // 方法注解可以覆蓋類型注解 Method m = clazz.getMethod(method.getName(), types); if (m != null && m.isAnnotationPresent(DataSource.class)) { DataSource source = m.getAnnotation(DataSource.class); DynamicDataSourceHolder.setDataSource(source.value()); } } catch (Exception e) { System.out.println(clazz + ":" + e.getMessage()); } } }
3、DynamicDataSourceHolder.java
public class DynamicDataSourceHolder { /** * 注意:數據源標識保存在線程變量中,避免多線程操作數據源時互相干擾 */ private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>(); public static String getDataSource() { return THREAD_DATA_SOURCE.get(); } public static void setDataSource(String dataSource) { THREAD_DATA_SOURCE.set(dataSource); } public static void clearDataSource() { THREAD_DATA_SOURCE.remove(); } }