1. 程式人生 > >spring boot+mybatis+druid 多資料來源多庫分散式事務

spring boot+mybatis+druid 多資料來源多庫分散式事務

廢話不多說,首先貼配置檔案,需要引入pomxml

        <dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.10</version>
		</dependency>
		<!-- JTA atomikos事務 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jta-atomikos</artifactId>
		</dependency>

JTA/atomikos自行了解

配置檔案:

spring: 
    datasource:
        druid: 
          type: com.alibaba.druid.pool.xa.DruidXADataSource
          driver-class-name: com.mysql.jdbc.Driver
          platform: mysql
          default: 
            # 資源標識
            uniqueResourceName: default
            # 資料來源類名
            xaDataSourceClassName: com.alibaba.druid.pool.xa.DruidXADataSource
#            xaDataSourceClassName: org.apache.tomcat.jdbc.pool.DataSource
#            xaDataSourceClassName: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
            xaProperties: 
              # 資料來源配置
              driverClassName: com.mysql.jdbc.Driver
              url: jdbc:mysql://211.149.199.68:3306/mypinyu?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&pinGlobalTxToPhysicalConnection=true
              username: root
              password: 
[email protected]
initialSize: 10 minIdle: 10 maxActive: 200 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false # 開啟PSCache,並且指定每個連線上PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 # 通過connectProperties屬性來開啟mergeSql功能;慢SQL記錄 connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 #超過時間限制是否回收 removeAbandoned: true #超時時間;單位為秒。180秒=3分鐘 removeAbandonedTimeout: 180 #關閉abanded連線時輸出錯誤日誌 # logAbandoned: true second: # 資源標識 uniqueResourceName: second # 資料來源類名 xaDataSourceClassName: com.alibaba.druid.pool.xa.DruidXADataSource # xaDataSourceClassName: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource # xaDataSourceClassName: org.apache.tomcat.jdbc.pool.DataSource xaProperties: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mypinyu?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&pinGlobalTxToPhysicalConnection=true username: root password: admin initialSize: 10 minIdle: 10 maxActive: 200 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 #驗證連線是否可用,使用的SQL語句 validationQuery: SELECT 1 FROM DUAL #DruidXADataSource pool.DataSource 範圍 # testQuery: select 1#MysqlXADataSource範圍使用 #指明連線是否被空閒連接回收器(如果有)進行檢驗.如果檢測失敗,則連線將被從池中去除. testWhileIdle: true #借出連線時不要測試,否則很影響效能 testOnBorrow: false testOnReturn: false # 開啟PSCache,並且指定每個連線上PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 # 通過connectProperties屬性來開啟mergeSql功能;慢SQL記錄 connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 #設定連線引數,否則durid在mysql斷開連線後連線不上資料庫了,配置資料庫斷開後自動連線 #超過時間限制是否回收 removeAbandoned: true #超時時間;單位為秒。180秒=3分鐘 removeAbandonedTimeout: 180 #關閉abanded連線時輸出錯誤日誌 # logAbandoned: true #jta相關引數配置 jta: log-dir: classpath:tx-logs transaction-manager-id: transactionManager logging.config: classpath: log4j2.xml #返回檢視的字首 目錄對應src/main/webapp下 spring.mvc.view.prefix: /WEB-INF/jsp/ #返回的字尾 spring.mvc.view.suffix: .jsp

配置:

package com.pinyu.system.global.config.datasource;

import java.util.Properties;

import javax.sql.DataSource;
import javax.transaction.UserTransaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.transaction.jta.JtaTransactionManager;

import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;

/** 
* @author ypp
* 建立時間:2018年11月9日 上午9:36:21 
* @Description: TODO(用一句話描述該檔案做什麼) 
*/
@Configuration
public class DruidConfig {

	@Bean(DataSourceNames.DEFAULT)
    @Primary
    @Autowired
    public DataSource systemDataSource(Environment env) {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        Properties prop = build(env, "spring.datasource.druid."+DataSourceNames.DEFAULT+".");
        ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        ds.setUniqueResourceName(DataSourceNames.DEFAULT);
        ds.setPoolSize(5);
        ds.setXaProperties(prop);
        return ds;

    }

    @Autowired
    @Bean(name = DataSourceNames.SECOND)
    public AtomikosDataSourceBean businessDataSource(Environment env) {

        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        Properties prop = build(env, "spring.datasource.druid."+DataSourceNames.SECOND+".");
        ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        ds.setUniqueResourceName(DataSourceNames.SECOND);
        ds.setPoolSize(5);
        ds.setXaProperties(prop);

        return ds;
    }


    /**
     * 注入事物管理器
     * @return
     */
    @Bean("transactionManager")
    public JtaTransactionManager regTransactionManager () {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        UserTransaction userTransaction = new UserTransactionImp();
        return new JtaTransactionManager(userTransaction, userTransactionManager);
    }


    private Properties build(Environment env, String prefix) {
    	long minEvictableIdleTimeMillis = Long.parseLong(env.getProperty(prefix + "minEvictableIdleTimeMillis"));
    	long timeBetweenEvictionRunsMillis = Long.parseLong(env.getProperty(prefix + "timeBetweenEvictionRunsMillis"));
        Properties prop = new Properties();
        prop.put("url", env.getProperty(prefix + "url"));
        prop.put("username", env.getProperty(prefix + "username"));
        prop.put("password", env.getProperty(prefix + "password"));
        prop.put("driverClassName", env.getProperty(prefix + "driverClassName"));
        prop.put("initialSize", Integer.valueOf(env.getProperty(prefix + "initialSize")));
        prop.put("minIdle", Integer.valueOf(env.getProperty(prefix + "minIdle")));
        prop.put("maxActive", Integer.valueOf(env.getProperty(prefix + "maxActive")));
        prop.put("maxWait", Integer.valueOf(env.getProperty(prefix + "maxWait")));
//        prop.put("timeBetweenEvictionRunsMillis", timeBetweenEvictionRunsMillis);
//        prop.put("minEvictableIdleTimeMillis", minEvictableIdleTimeMillis);
        prop.put("validationQuery", env.getProperty(prefix + "validationQuery"));
        prop.put("testWhileIdle", Boolean.valueOf(env.getProperty(prefix + "testWhileIdle")));
        prop.put("testOnBorrow", Boolean.valueOf(env.getProperty(prefix + "testOnBorrow")));
        prop.put("testOnReturn", Boolean.valueOf(env.getProperty(prefix + "testOnReturn")));
        return prop;
    }

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        //控制檯管理使用者,加入下面2行 進入druid後臺就需要登入
        //servletRegistrationBean.addInitParameter("loginUsername", "admin");
        //servletRegistrationBean.addInitParameter("loginPassword", "admin");
        return servletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        filterRegistrationBean.addInitParameter("profileEnable", "true");
        return filterRegistrationBean;
    }

    @Bean
    public StatFilter statFilter(){
        StatFilter statFilter = new StatFilter();
        statFilter.setLogSlowSql(true); //slowSqlMillis用來配置SQL慢的標準,執行時間超過slowSqlMillis的就是慢。
        statFilter.setMergeSql(true); //SQL合併配置
        statFilter.setSlowSqlMillis(1000);//slowSqlMillis的預設值為3000,也就是3秒。
        return statFilter;
    }

    @Bean
    public WallFilter wallFilter(){
        WallFilter wallFilter = new WallFilter();
        //允許執行多條SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);
        wallFilter.setConfig(config);
        return wallFilter;
    }
}
package com.pinyu.system.global.config.datasource;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

/** 
* @author ypp
* 建立時間:2018年11月9日 上午10:09:56 
* @Description: TODO(用一句話描述該檔案做什麼) 
*/
@Configuration
@MapperScan(basePackages = "com.pinyu.system.mapper",sqlSessionFactoryRef = "defaultSqlSessionFactory")
public class MybatisDataSourceDefaultConfig {


    @Bean
    public SqlSessionFactory defaultSqlSessionFactory(@Qualifier(DataSourceNames.DEFAULT)DataSource ds) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(ds);
        //指定mapper xml目錄
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/**/*Mapper.xml"));
        return factoryBean.getObject();

    }

    @Bean
    public SqlSessionTemplate defaultSqlSessionTemplate(@Qualifier("defaultSqlSessionFactory")SqlSessionFactory sf) throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sf); // 使用上面配置的Factory
        return template;
    }

    //關於事務管理器,不管是JPA還是JDBC等都實現自介面 PlatformTransactionManager
    // 如果你新增的是 spring-boot-starter-jdbc 依賴,框架會預設注入 DataSourceTransactionManager 例項。
    //在Spring容器中,我們手工註解@Bean 將被優先載入,框架不會重新例項化其他的 PlatformTransactionManager 實現類。
    /*@Bean(name = "transactionManager2")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        //MyBatis自動參與到spring事務管理中,無需額外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的資料來源
        // 與DataSourceTransactionManager引用的資料來源一致即可,否則事務管理會不起作用。
        return new DataSourceTransactionManager(ds);
    }*/

}
package com.pinyu.system.global.config.datasource;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

/** 
* @author ypp
* 建立時間:2018年11月9日 上午10:18:03 
* @Description: TODO(用一句話描述該檔案做什麼) 
*/
@Configuration
@MapperScan(basePackages = "com.pinyu.system.mapper2",sqlSessionFactoryRef = "secondSqlSessionFactory")
public class MybatisDataSourceSecondConfig {

	@Bean
    public SqlSessionFactory secondSqlSessionFactory(@Qualifier(DataSourceNames.SECOND)DataSource ds) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(ds);
        //指定mapper xml目錄
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath:mapper2/**/*Mapper.xml"));
        return factoryBean.getObject();

    }

    @Bean
    public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory")SqlSessionFactory sf) throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sf); // 使用上面配置的Factory
        return template;
    }

    //關於事務管理器,不管是JPA還是JDBC等都實現自介面 PlatformTransactionManager
    // 如果你新增的是 spring-boot-starter-jdbc 依賴,框架會預設注入 DataSourceTransactionManager 例項。
    //在Spring容器中,我們手工註解@Bean 將被優先載入,框架不會重新例項化其他的 PlatformTransactionManager 實現類。
    /*@Bean(name = "transactionManager2")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        //MyBatis自動參與到spring事務管理中,無需額外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的資料來源
        // 與DataSourceTransactionManager引用的資料來源一致即可,否則事務管理會不起作用。
        return new DataSourceTransactionManager(ds);
    }*/
}
package com.pinyu.system.global.config.datasource;

/**
 * @author ypp 建立時間:2018年11月6日 上午11:39:03
 * @Description: TODO(資料來源常量池)
 */
public interface DataSourceNames {

	public static final String DEFAULT = "default";

	public static final String SECOND = "second";

}

啟動類:

package com.pinyu.system;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.pinyu.system.global.config.datasource.DruidConfig;

@ComponentScan(basePackages = "com.pinyu.system")
@EnableTransactionManagement(proxyTargetClass = true)
//@SpringBootApplication(exclude={  
//		DataSourceAutoConfiguration.class,  
////		HibernateJpaAutoConfiguration.class, //(如果使用Hibernate時,需要加)  
//		DataSourceTransactionManagerAutoConfiguration.class  
//		})
@SpringBootApplication
@Import(DruidConfig.class)
public class Application extends SpringBootServletInitializer {

}

springboot 檢測到jta atomikos環境時,會自動啟用分散式事務,給相應的配置,以上配置也是自己摸索出來的,也不敢太深入講解,怕說錯。但親測可用,多庫使用事務也可以回滾。