Spring + spring MVC + mybatis + Oracle + atomikos + JTA to realize multi-data source transaction management

Keywords: Java Spring xml Mybatis Oracle

- Restore content to start

In the process of project, we encountered the situation that we need to access two databases in a project. We found that the transaction could not be rolled back properly because of the routine spring management. Therefore, we adopt the distributed data source mode of jta+atomikos to manage the transaction. Make a record here for future reference. Please forgive me for the improper use of words in this article.

Configuration file:

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">

   <!-- Load Spring configuration file -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/spring.xml
        </param-value>
    </context-param>
    


    <!-- Character Set Filter -->
    <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 Monitor -->
    <listener>
        <description>Spring Monitor</description>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Prevent Spring Memory overflow listener -->
    <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 timeout -->
    <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>
<!-- Add to Hibernate rely on -->    
    <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>
        <!-- Newly added -->
        <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>

        

        <!-- check -->
        <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 Generating Needs Dependency -->
        <!-- template engine -->
        <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 Transaction Management of Multiple Data Sources -->        
        <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

Configuring data source 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. Add jta.properties file to the resources directory

  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 package(Automatic injection) -->
    <context:component-scan base-package="com.zzxw.web.controller">
    </context:component-scan>

    <mvc:annotation-driven>
        <!-- Handle responseBody Date type inside -->
        <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>

    <!-- Static resource allocation -->
    <mvc:resources mapping="/resources/**" location="/resources/"/>

    <!-- Analysis of Model View Names,That is to add prefix and suffix to the model view name -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- Upload restriction -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- Upload file size limit is 31 M,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>
    
    <!-- Total interceptor  -->
    <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

   <!-- start-up AspectJ Support   
    <aop:aspectj-autoproxy />--> 
    <context:property-placeholder location="classpath:config.properties" />
     <!-- Enable CGliB -->
<!--     <aop:aspectj-autoproxy  /> -->
    
    
    <!-- Common configuration of multiple data sources for direct reference below -->
     <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> 
<!--data source A--> <bean id="dataSourceA" parent="abstractXADataSource"> <!-- value As long as multiple XA Do not duplicate data sources, just name them. --> <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> <!--data source 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 integration Mybatis,See more documentation: http://mp.baomidou.com --> <bean id="sqlSessionFactory_A" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceA"/> <!-- Automatic scanning Mapping.xml file --> <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> <!-- Paging plug-in configuration --> <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"> <property name="dialectType" value="oracle"/> </bean> </array> </property> <!-- Global configuration injection --> <property name="globalConfig" ref="globalConfig" /> </bean> <!-- Spring integration Mybatis,See more documentation: http://mp.baomidou.com --> <bean id="sqlSessionFactory_B" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceB"/> <!-- Automatic scanning Mapping.xml file --> <property name="mapperLocations" value="classpath:mybatis/risk/*.xml"/> <property name="configLocation" value="classpath:spring/mybatis-config.xml"/>
<!--Corresponding entity classes--> <property name="typeAliasesPackage" value="com.zzxw.web.domain"/> <property name="plugins"> <array> <!-- Paging plug-in configuration --> <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"> <property name="dialectType" value="oracle"/> </bean> </array> </property> <!-- Global configuration injection --> <property name="globalConfig" ref="globalConfig" /> </bean> <!-- Configure multiple data sources MultipleDataSource--> <bean name="dynamicDatasource" class="com.zzxw.web.common.DynamicDataSource"> <property name="targetDataSources"> <map> <!-- key and value-ref Try to be consistent. I made an error because of the inconsistent name during the test. //After searching for a long time without finding the reason, the unified name finally started successfully. --> <entry key="dataSourceA" value-ref="dataSourceA"></entry> <entry key="dataSourceB" value-ref="dataSourceB"></entry> </map> </property> <!-- Specify a default data source, which is the default data source used by the local system when there is no need to switch the data source --> <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> <!-- Configure custom SqlSessionTemplate Template, injection related configuration --> <bean id="sqlSessionTemplate" class="com.zzxw.web.common.CustomSqlSessionTemplate" scope="prototype"> <!-- Construct the injection parameter to specify the local default data source SqlSessionFactoryBean --> <constructor-arg ref="sqlSessionFactory_A" /> <property name="targetSqlSessionFactorys"> <map> <!-- key And the data source configured above id Values are as consistent as possible, and I report errors consistently when testing because of inconsistent names. //After searching for a long time without finding the reason, the unified name finally started successfully. --> <entry value-ref="sqlSessionFactory_A" key="dataSourceA"/> <entry value-ref="sqlSessionFactory_B" key="dataSourceB"/> </map> </property> </bean> <!-- MyBatis Dynamic scanning --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.zzxw.web.**.mapper"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> <!-- jta Configuration start --> <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 End of configuration --> <!-- Configuration transaction management --> <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`("data base ID Self increment") INPUT->`1`(User input ID") ID_WORKER->`2`("Global uniqueness ID") UUID->`3`("Global uniqueness 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 This item needs to be added --> <property name="dbType" value="oracle" /> <!-- Global Table Naming Settings for Underlines 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">
<!--
 The location of plugins in the configuration file must meet the requirements, otherwise errors will be reported in the following order:
 |   properties?, settings?,
 |   typeAliases?, typeHandlers?,
 |   objectFactory?,objectWrapperFactory?,
 |   plugins?,
 |   environments?, databaseIdProvider?, mappers?
 |-->
<configuration>
    <!--
     Global Configuration Settings
     |
     Default value of configurable options, description
     |
     AggressiveLazy Loading true, when set to'true', lazy objects may be loaded by any lazy attribute. Otherwise, each attribute is loaded on demand.
     MultipleResultSets Enabled true, allowing and disallowing single statements to return multiple data sets (depending on driver requirements)
     Use Column Label true, using column labels instead of column names. Different drives do different things. Refer to the driver documentation or test with these two different options.
     UseGeneratedKeys false, which allows JDBC to generate primary keys. Driver support is required. If set to true, this setting forces the generated primary key to be used, and some drives are incompatible but still executable.
     AutoMapping Behavior PARTIAL specifies whether and how MyBatis automatically maps properties of data table fields and objects. PARTIAL will only automatically map simple, no nested results. FULL automatically maps all complex results.
     Default ExecutorType SIMPLE, configure and set the executor, and the SIMPLE executor executes other statements. REUSE executors may reuse prepared statements, and BATCH executors may repeat statements and batch updates.
     Default Statement Timeout null, set a time limit to determine how long the driver will wait for the database response to be timed out
     | -->
    <settings>
        <! - This configuration enables global mappers to enable or disable caching - >
        <setting name="cacheEnabled" value="true"/>
        <! - Enable or disable lazy loading globally. When disabled, all associated objects are loaded immediately - >.
        <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

    <!-- Introducing attribute files -->
    <context:property-placeholder location="classpath:config.properties"/>

    <!-- Service package(Automatic injection) -->
    <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());
            }
        }
    }
 
}

Multi-source switching code

1,DynamicDataSource.java

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // Obtaining data source identifiers from a custom location
        return DynamicDataSourceHolder.getDataSource();
    }

}

2,DataSourceAspect.java

public class DataSourceAspect {

    /**
     * Intercept the target method, get the data source identifier specified by @DataSource, and set it to the thread store to switch the data source
     * 
     * @param point
     * @throws Exception
     */
    public void intercept(JoinPoint point) throws Exception {
        Class<?> target = point.getTarget().getClass();
        MethodSignature signature = (MethodSignature) point.getSignature();
        // By default, annotations of the target type are used, and if not, annotations of the implementation interface are used.
        for (Class<?> clazz : target.getInterfaces()) {
            resolveDataSource(clazz, signature.getMethod());
        }
        resolveDataSource(target, signature.getMethod());
    }

    /**
     * Extracting Data Source Identification in Target Object Method Annotations and Type Annotations
     * 
     * @param clazz
     * @param method
     */
    private void resolveDataSource(Class<?> clazz, Method method) {
        try {
            Class<?>[] types = method.getParameterTypes();
            // Default use type annotation
            if (clazz.isAnnotationPresent(DataSource.class)) {
                DataSource source = clazz.getAnnotation(DataSource.class);
                DynamicDataSourceHolder.setDataSource(source.value());
            }
            // Method annotations can override type annotations
            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 {
    /**
     * Note: Data source identifiers are stored in thread variables to avoid interference when multithreading data sources
     */
    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();
    }

}

Posted by Cep on Fri, 14 Dec 2018 22:54:04 -0800