1. 程式人生 > >spring-boot整合mybatis和druid連線池(多資料來源)

spring-boot整合mybatis和druid連線池(多資料來源)

上一篇文章介紹了 spring-boot整合mybatis和druid連線池如何配置和使用,本篇文章是上篇的擴充套件,如果業務中出現了需要從多個數據源中獲取資料,到底該如何實現?本文主要介紹在一種最為簡單的實現方案:多資料來源 - 多例項。 

在上篇文章中不難看出Spring Boot中,通過為該資料來源DataSource初始化一個與之對應的SessionFactory,從而實現連線。因此在面對多資料來源的時候,可以分別為每個資料來源寫一個mybatis的config類,使其每個DataSource都擁有一個只屬於自己的SessionFactory,這樣就可以根據各自的mapper(dao)對映目錄找到對應的mybaits例項。

注:這種實現方法要求不同的mybatis例項的mapper對映目錄(dao)不能相同。


1、pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.edu.nuc</groupId>
  <artifactId>Test1</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>Test1</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    
    <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
    <mysql-connector>5.1.39</mysql-connector>
  </properties>

  <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.5.RELEASE</version>
  </parent>
  
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
    
    <!-- spring boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>  
            <exclusion>  
                <groupId>org.springframework.boot</groupId>  
                <artifactId>spring-boot-starter-logging</artifactId>  
            </exclusion>  
        </exclusions>  
    </dependency>
     <!-- Spring Boot log4j依賴 -->  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-log4j</artifactId>
        <version>1.3.8.RELEASE</version>
    </dependency> 
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-tomcat</artifactId>
	    <scope>provided</scope>
	</dependency>
	<!-- jsp -->
	<dependency>
	   <groupId>org.apache.tomcat.embed</groupId>
	    <artifactId>tomcat-embed-jasper</artifactId>
	    <scope>provided</scope>
	</dependency>
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>jstl</artifactId>
	</dependency>
    
    <!-- Spring Boot Mybatis 依賴 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis-spring-boot}</version>
    </dependency>
    <!-- Druid 資料連線池依賴 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.13</version>
    </dependency>
     <!-- MySQL 連線驅動依賴 -->
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
     </dependency>
    
  </dependencies>

  <build>
  	<finalName>Test</finalName>
  	<plugins>
        <!-- <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin> -->
        <plugin>
		    <groupId>org.apache.maven.plugins</groupId>
		    <artifactId>maven-war-plugin</artifactId>
		    <configuration>
		        <failOnMissingWebXml>false</failOnMissingWebXml>
		    </configuration>
		</plugin>
    </plugins>
  </build>
</project>

2、application.properties和資料來源配置:

1)application.properties配置:

#---------------------ds1資料來源(使用durid連線池)
ds1.datasource.url=jdbc:mysql://ds1.w.abc.db:1883/ttengine?useUnicode=true&characterEncoding=utf8
ds1.datasource.username=ds1
ds1.datasource.password=ds1
ds1.datasource.driverClassName=com.mysql.jdbc.Driver

ds1.datasource.initialSize=20
ds1.datasource.minIdle=20
ds1.datasource.maxActive=200
ds1.datasource.maxWait=60000
ds1.datasource.timeBetweenEvictionRunsMillis=60000
ds1.datasource.minEvictableIdleTimeMillis=300000
ds1.datasource.testWhileIdle=true
ds1.datasource.testOnBorrow=false
ds1.datasource.testOnReturn=false
ds1.datasource.poolPreparedStatements=true
ds1.datasource.maxPoolPreparedStatementPerConnectionSize=20

ds2.datasource.url=jdbc:mysql://ds2.w.abc.db:8907/toutiao?useUnicode=true&characterEncoding=utf8
ds2.datasource.username=ds2
ds2.datasource.password=^ds2
ds2.datasource.driverClassName=com.mysql.jdbc.Driver
#---------------------ds2資料來源
ds2.datasource.initialSize=20
ds2.datasource.minIdle=20
ds2.datasource.maxActive=200
ds2.datasource.maxWait=60000
ds2.datasource.timeBetweenEvictionRunsMillis=60000
ds2.datasource.minEvictableIdleTimeMillis=300000
ds2.datasource.testWhileIdle=true
ds2.datasource.testOnBorrow=false
ds2.datasource.testOnReturn=false
ds2.datasource.poolPreparedStatements=true
ds2.datasource.maxPoolPreparedStatementPerConnectionSize=20


# 頁面預設字首目錄
spring.mvc.view.prefix=/WEB-INF/page/
spring.mvc.view.suffix=.jsp

由於我們使用druid連線池,所以我們需要定製DataSourceConfig(每個資料來源都要定製一份)

2)Datasource1Config:

package nc.edu.nuc.Test.dao.mysql;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
//掃描 Mapper 介面並容器管理
@MapperScan(basePackages = Datasource1Config.PACKAGE, sqlSessionFactoryRef = "ds1SqlSessionFactory")
public class Datasource1Config {
	// 精確到 master 目錄,以便跟其他資料來源隔離
    static final String PACKAGE = "nc.edu.nuc.Test.dao.mysql.ds1";
    static final String MAPPER_LOCATION = "classpath:mapper/ds1/*.xml";

    @Value("${ds1.datasource.url}")
    private String url;
    @Value("${ds1.datasource.username}")
    private String user;
    @Value("${ds1.datasource.password}")
    private String password;
    @Value("${ds1.datasource.driverClassName}")
    private String driverClass;
    
    @Value("${ds1.datasource.maxActive}")
    private Integer maxActive;
    @Value("${ds1.datasource.minIdle}")
    private Integer minIdle;
    @Value("${ds1.datasource.initialSize}")
    private Integer initialSize;
    @Value("${ds1.datasource.maxWait}")
    private Long maxWait;
    @Value("${ds1.datasource.timeBetweenEvictionRunsMillis}")
    private Long timeBetweenEvictionRunsMillis;
    @Value("${ds1.datasource.minEvictableIdleTimeMillis}")
    private Long minEvictableIdleTimeMillis;
    @Value("${ds1.datasource.testWhileIdle}")
    private Boolean testWhileIdle;
    @Value("${ds1.datasource.testWhileIdle}")
    private Boolean testOnBorrow;
    @Value("${ds1.datasource.testOnBorrow}")
    private Boolean testOnReturn;

    @Bean(name = "ds1DataSource")
    @Primary
    public DataSource ds1DataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        
        //連線池配置
        dataSource.setMaxActive(maxActive);
        dataSource.setMinIdle(minIdle);
        dataSource.setInitialSize(initialSize);
        dataSource.setMaxWait(maxWait);
        dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        dataSource.setTestWhileIdle(testWhileIdle);
        dataSource.setTestOnBorrow(testOnBorrow);
        dataSource.setTestOnReturn(testOnReturn);
        dataSource.setValidationQuery("SELECT 'x'");
        
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
        
        return dataSource;
    }

    @Bean(name = "ds1TransactionManager")
    @Primary
    public DataSourceTransactionManager ds1TransactionManager() {
        return new DataSourceTransactionManager(ds1DataSource());
    }

    @Bean(name = "ds1SqlSessionFactory")
    @Primary
    public SqlSessionFactory ds1SqlSessionFactory(@Qualifier("ds1DataSource") DataSource ds1DataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(ds1DataSource);
        sessionFactory.setTypeAliasesPackage("nc.edu.nuc.Test.entity");
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(Datasource1Config.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}
3)Datasource2Config:
package nc.edu.nuc.Test.dao.mysql;

import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
//掃描 Mapper 介面並容器管理
@MapperScan(basePackages = Datasource2Config.PACKAGE, sqlSessionFactoryRef = "ds2SqlSessionFactory")
public class Datasource2Config {
	// 精確到 master 目錄,以便跟其他資料來源隔離
    static final String PACKAGE = "nc.edu.nuc.Test.dao.mysql.ds2";
    static final String MAPPER_LOCATION = "classpath:mapper/ds2/*.xml";

    @Value("${ds2.datasource.url}")
    private String url;
    @Value("${ds2.datasource.username}")
    private String user;
    @Value("${ds2.datasource.password}")
    private String password;
    @Value("${ds2.datasource.driverClassName}")
    private String driverClass;
    
    @Value("${ds2.datasource.maxActive}")
    private Integer maxActive;
    @Value("${ds2.datasource.minIdle}")
    private Integer minIdle;
    @Value("${ds2.datasource.initialSize}")
    private Integer initialSize;
    @Value("${ds2.datasource.maxWait}")
    private Long maxWait;
    @Value("${ds2.datasource.timeBetweenEvictionRunsMillis}")
    private Long timeBetweenEvictionRunsMillis;
    @Value("${ds2.datasource.minEvictableIdleTimeMillis}")
    private Long minEvictableIdleTimeMillis;
    @Value("${ds2.datasource.testWhileIdle}")
    private Boolean testWhileIdle;
    @Value("${ds2.datasource.testWhileIdle}")
    private Boolean testOnBorrow;
    @Value("${ds2.datasource.testOnBorrow}")
    private Boolean testOnReturn;

    @Bean(name = "ds2DataSource")
    public DataSource ds2DataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        
        //連線池配置
        dataSource.setMaxActive(maxActive);
        dataSource.setMinIdle(minIdle);
        dataSource.setInitialSize(initialSize);
        dataSource.setMaxWait(maxWait);
        dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        dataSource.setTestWhileIdle(testWhileIdle);
        dataSource.setTestOnBorrow(testOnBorrow);
        dataSource.setTestOnReturn(testOnReturn);
        dataSource.setValidationQuery("SELECT 'x'");
        
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
        
        return dataSource;
    }

    @Bean(name = "ds2TransactionManager")
    public DataSourceTransactionManager ds2TransactionManager() {
        return new DataSourceTransactionManager(ds2DataSource());
    }

    @Bean(name = "ds2SqlSessionFactory")
    public SqlSessionFactory ds2SqlSessionFactory(@Qualifier("ds2DataSource") DataSource ds2DataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(ds2DataSource);
        sessionFactory.setTypeAliasesPackage("nc.edu.nuc.Test.entity");
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(Datasource2Config.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}

多資料來源配置的時候注意,必須要有一個主資料來源。

  • @Primary 標誌這個 Bean 如果在多個同類 Bean 候選時,該 Bean 優先被考慮。「多資料來源配置的時候注意,必須要有一個主資料來源,用 @Primary 標誌該 Bean」
  • @MapperScan 掃描 Mapper 介面並容器管理,包路徑精確到 master,為了和下面 cluster 資料來源做到精確區分
  • @Value 獲取全域性配置檔案 application.properties 的 kv 配置,並自動裝配sqlSessionFactoryRef 表示定義了 key ,表示一個唯一 SqlSessionFactory 例項.


4)說明:

  • 上面資料配置分別掃描nc.edu.nuc.Test.dao.mysql.ds1 和 nc.edu.nuc.Test.dao.mysql.ds2中定義的Mapper介面TestDao和DemoDao(可以在介面上配置@Mapper註解,也可以在springboot主類上統一加@MapperScan("..."))
  • ds1對應 xml classpath:mapper/ds1下的sql檔案,ds2對應 xml classpath:mapper/ds2下的sql檔案,Mybatis 內部會使用反射機制執行去解析相應 SQL。

3、其他:

多資料來源配置好後,接下來就和正常spring-boot的用法一樣了。整個結構:


1)crontroller:

package nc.edu.nuc.Test.controller;

import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import nc.edu.nuc.Test.entity.Test;
import nc.edu.nuc.Test.service.TestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
	private static final Logger logger = LoggerFactory.getLogger(TestController.class); 
	
	@Autowired
	private TestService testService;
	
	@RequestMapping("/test")
    @ResponseBody
    public String test(HttpServletRequest request) {
		String name = request.getParameter("name");
		Test test = null;
		try {
			test = testService.findByName(name);
		} catch (Exception e) {
			test = new Test();
			logger.error("test error.",e);
		}
		logger.info("test....{}",name);
        return test.toString();
    }
}

2)service:

package nc.edu.nuc.Test.service;

import java.util.List;
import nc.edu.nuc.Test.dao.mysql.ds1.TestDao;
import nc.edu.nuc.Test.dao.mysql.ds2.DemoDao;
import nc.edu.nuc.Test.entity.Demo;
import nc.edu.nuc.Test.entity.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TestService{
	private static final Logger logger = LoggerFactory.getLogger(TestService.class); 
	
	@Autowired  
	private TestDao testDao;  
	@Autowired  
	private DemoDao demoDao;  
	
	public Test findByName(String name) throws Exception{
		List<Demo> demoList = demoDao.getDemoList();
		logger.info(demoList.toString());
		return testDao.findByName(name);
	}
}

3)entity:

package nc.edu.nuc.Test.entity;

public class Demo {
	private Long id;
	private String title;
	private String descs;
	
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getDescs() {
		return descs;
	}
	public void setDescs(String descs) {
		this.descs = descs;
	}
	
	@Override
	public String toString() {
		return "Demo [id=" + id + ", title=" + title + ", descs=" + descs + "]";
	}
}
package nc.edu.nuc.Test.entity;

public class Test {
	private Long id;
	private String name;
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Test [id=" + id + ", name=" + name + "]";
	}
}

4)dao:

package nc.edu.nuc.Test.dao.mysql.ds1;

import nc.edu.nuc.Test.entity.Test;
import org.apache.ibatis.annotations.Param;

public interface TestDao {
    Test findByName(@Param("name") String n);
}
package nc.edu.nuc.Test.dao.mysql.ds2;

import java.util.List;
import nc.edu.nuc.Test.entity.Demo;

public interface DemoDao {
	List<Demo> getDemoList() throws Exception;
}

5)mapper sql檔案:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >  
<mapper namespace="nc.edu.nuc.Test.dao.mysql.ds1.TestDao">  
    <resultMap id="BaseResultMap" type="nc.edu.nuc.Test.entity.Test">  
        <result column="id" property="id" />  
        <result column="name" property="name" />  
    </resultMap>  
  
    <parameterMap id="Test" type="nc.edu.nuc.Test.entity.Test"/>  
  
    <sql id="Base_Column_List">  
        id,name  
    </sql>  
    <select id="findByName" resultMap="BaseResultMap" parameterType="java.lang.String">  
        select  
        <include refid="Base_Column_List" />  
        from test  
        where name = #{name}  
    </select>  
</mapper>  
<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >  
<mapper namespace="nc.edu.nuc.Test.dao.mysql.ds2.DemoDao">  
    <resultMap id="BaseResultMap" type="nc.edu.nuc.Test.entity.Demo">  
        <result column="id" property="id" />  
        <result column="title" property="title" />  
        <result column="descs" property="descs" />  
    </resultMap>  
  
    <parameterMap id="Demo" type="nc.edu.nuc.Test.entity.Demo"/>  
  
    <sql id="Base_Column_List">  
        id,title,descs
    </sql>  
  
    <select id="getDemoList" resultType="Demo">  
        select  
        <include refid="Base_Column_List" />  
        from demo  
    </select>  
  
</mapper>  

6)app啟動類:

package nc.edu.nuc.Test;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Hello world!
 * 可以統一用@MapperScan指定掃描的dao,也可以在每個dao上新增@Mapper
 */
@SpringBootApplication
//mapper 介面類掃描包配置
@MapperScan("nc.edu.nuc.Test.dao.mysql")
public class App {
    public static void main( String[] args ) {
    	SpringApplication.run(App.class, args);
    }
}

參考:https://github.com/YHYR/Mybatis

https://github.com/JeffLi1993/springboot-learning-example/blob/master/springboot-mybatis-mutil-datasource/src/main/java/org/spring/springboot/service/impl/UserServiceImpl.java

https://www.bysocket.com/?p=1712

https://blog.csdn.net/YHYR_YCY/article/details/78894940