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