Spring配置多数据源
环境背景
这里以配置两个mysql数据库为展示用例。持久层使用mybatis实现。两个连接分别使用不同的连接池 druid 和 hikari
相关知识
这里介绍了一些相关的知识点,清楚后可以跳过
mybatis和mybatis-spring-boot-starter的关系
在pom依赖上它们是两个不同的依赖文件。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
mybatis-spring-boot-starter类似一个中间件,链接Springboot和mybatis,构建基于Springboot的mybatis应用程序。同时mybatis-spring-boot-starter作为一个集成包是包含mybatis的。
mybatis-spring-boot-starter和spring-boot-starter-jdbc
mybatis-spring-boot-starter作为一个集成包,如果在项目里引入了该依赖那就不需要在显示的依赖spring-boot-starter-jdbc,因为mybatis-spring-boot-starter是包含spring-boot-starter-jdbc的。
mybatis-spring-boot-starter的集成
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2015-2018 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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>
<parent>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot</artifactId>
<version>1.3.2</version>
</parent>
<artifactId>mybatis-spring-boot-starter</artifactId>
<name>mybatis-spring-boot-starter</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
</dependencies>
</project>
作为一个集成包,可以看到包含了mybatis、mybatis-spring、spring-boot-starter-jdbc。
Spring自动装配的相关注解
@Autowired
- 默认是按照类型进行配置注入,默认情况下,它要求依赖对象必须存在,如果允许为null值,可以设置它的 required 为false
- 如果想要按照名称进行装配的话,可以添加一个@Qualifier注解解决
@Qualifire
- 让Spring可以按照Bean名称注入
/**
* 得到数据源
* 定义一个名字为barDataSource的数据源
*
* @return
*/
@Bean("barDataSource")
public DataSource barDataSource() {
DataSourceProperties dataSourceProperties = barDataSourceProperties();
log.info("bar datasource url:{}", dataSourceProperties.getUrl());
log.info("{}", dataSourceProperties.getType().getName());
return dataSourceProperties.initializeDataSourceBuilder().build();
}
/**
* 定义一个名字为 barSqlSessionFactory 的SqlSession工厂,实现的时候使用名字为barDataSource的数据源去实现
* @param dataSource
* @return
* @throws Exception
*/
@Bean("barSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("barDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
return bean.getObject();
}
- 首先定义一个名字为barDataSource的数据源
- 在定义SqlSession工厂时,通过
@Qualifier
指定使用名字为barDataSource
数据源对象
@Primary
- 当有多个相同类型的Bean时,优先使用@Primary注解的Bean
@Resource
- Resource有两个比较重要的属性
- name:当设置了name属性时,Spring会将name的属性解析为bean的名称,使用byName的自动注入策略
- type:当设置了type属性时,Spring会将type的属性解析为bean的类型,使用byType的自动注入策略
- 如果既没有设置name属性,又没有设置type属性,Spring通过反射机制使用byName自动注册策略
- 使用Resource装配时的装配顺序
- 如果同时制定了name和type,那么Spring将从容器中找到唯一匹配的bean进行装配,如果找不到则抛出异常
- 如果指定了name属性,则从容器中查找名称匹配的bean进行装配,找不到则抛出异常
- 如果指定了type属性,则从容器中查找类型匹配的唯一的bean进行装配,找不到或者找到多个都会抛出异常
- 如果都不指定,则会自动按照byName方式装配,如果没有匹配,则回退一个原始类型进行匹配,如果匹配则自动装配
@ConfigurationProperties和@PropertySource
@ConfigurationProperties和@PropertySource都是用来读取Spring的配置文件的,有三种配置方式
第一种
- 首先在resource中创建一个资源文件(*.properties文件),比如book.properties,书籍的名称、价钱、版本等信息
- 创建对应的配置类文件,使用@PropertySource指定配置资源的名称避免出现中文信息乱码可以设置编码信息,使用@ConfigurationProperties指定属性信息
package com.lucky.spring.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* Created by zhangdd on 2020/7/16
*/
@PropertySource(value = "book.properties",encoding = "utf-8")
@ConfigurationProperties(prefix = "book")
@Component
public class BookConfig {
private String name;
private String price;
private String version;
public String getName() {
return name;
}
public String getPrice() {
return price;
}
public String getVersion() {
return version;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(String price) {
this.price = price;
}
public void setVersion(String version) {
this.version = version;
}
}
测试用例
@RestController
public class BookController {
@Autowired
BookConfig bookConfig;
@GetMapping("/api/queryBook")
public String queryBook() {
StringBuilder builder = new StringBuilder();
StringBuilder bookInfo = builder.append(bookConfig.getName())
.append(",")
.append(bookConfig.getPrice())
.append(",")
.append(bookConfig.getVersion());
return bookInfo.toString();
}
}
第二种
- 首先在resources下创建config文件夹,在config文件夹下创建资源文件(*.properties文件),比如person.properties,配置人员的信息
- 创建对应的配置类文件,使用@PropertySource指定配置资源的名称(区别点在于这里要指定资源文件的路径信息)避免出现中文信息乱码可以设置编码信息,使用@ConfigurationProperties指定属性信息
person.name=张三
person.age=18
package com.lucky.spring.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* Created by zhangdd on 2020/7/16
*/
@Component
@PropertySource(value = "classpath:config/person.properties",encoding = "utf-8")
@ConfigurationProperties(prefix = "person")
public class PersonConfig {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
测试用例
public class PersonController {
@Autowired
PersonConfig personConfig;
@GetMapping("/api/queryPersonInfo")
public String queryPersonInfo() {
StringBuilder builder = new StringBuilder();
builder.append(personConfig.getName())
.append(",")
.append(personConfig.getAge());
return builder.toString();
}
}
第三种
- 直接将配置信息写在application.properties中,
- 创建对应的配置类文件,当写在此文件中时,不需要指明资源文件路径只需要使用@ConfigurationProperties指明前缀即可
car:
price: 12345678
name: 奥迪
package com.lucky.spring.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Created by zhangdd on 2020/7/16
*/
@Component
@ConfigurationProperties(prefix = "car")
public class CarConfig {
private String price;
private String name;
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试用例
package com.lucky.spring.controller;
import com.lucky.spring.config.CarConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by zhangdd on 2020/7/16
*/
@RestController
public class CarController {
@Autowired
CarConfig carConfig;
@GetMapping("/api/queryCarInfo")
public String queryCarInfo() {
StringBuilder builder = new StringBuilder();
builder.append(carConfig.getName())
.append(",")
.append(carConfig.getPrice());
return builder.toString();
}
}
多数据源配置使用
相关依赖配置
基于环境背景信息,mysql数据库,mybatis持久层,三方数据源druid。添加配置信息如下:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lucky</groupId>
<artifactId>04spring-multi-datasource</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<configuration>
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 添加对应配置信息
- mybatis-spring-boot-starter
- mysql-connector-java
- druid-spring-boot-starter
连接信息配置
既然是多数据源的配置操作,那就是配置数据源信息了。配置方式有两种:
- 在application.yml文件中配置
- 在业务代码里配置
这里以配置文件中为例说明。
datasource:
foo:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/readinglist?characterEncoding=utf8&useSSL=false
username: root
password: 12345678
bar:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8&useSSL=false
username: root
password: 12345678
- 定义了两个数据源信息foo、bar
- 不同的数据源配置自己的数据库信息和Datasource信息
使用配置信息
已经对信息进行了配置,下面就是对配置信息的使用了。
foo数据源的配置
package com.lucky.spring.config;
import lombok.extern.slf4j.Slf4j;
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.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 javax.sql.DataSource;
/**
* Created by zhangdd on 2020/7/13
*/
@Configuration
@Slf4j
@MapperScan(basePackages = "com.lucky.spring.dao.foo", sqlSessionFactoryRef = "fooSqlSessionFactory")
public class FooDataSourceConfig {
static final String MAPPER_LOCATION = "classpath:mapper/foo/*.xml";
//=========foo 数据源相关配置 start==================================================
@Bean
@Primary// 该注解是指当有多个相同的bean时,优先使用用@Primary注解的bean
@ConfigurationProperties("datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}
/**
* 返回 foo 对应的数据源
*
* @return
*/
@Bean("fooDataSource")
@Primary
public DataSource fooDataSource() {
DataSourceProperties dataSourceProperties = fooDataSourceProperties();
log.info("foo datasource url:{}", dataSourceProperties.getUrl());
log.info("{}", dataSourceProperties.getType().getName());
return dataSourceProperties.initializeDataSourceBuilder().build();
}
/**
* 返回foo 对应数据库的会话工厂
*
* @param ds
* @return
*/
@Bean(name = "fooSqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("fooDataSource") DataSource ds) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(ds);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
return bean.getObject();
}
/**
* foo 对应的数据库会话模版
*
* @param sessionFactory
* @return
*/
@Bean("fooSqlSessionTemplate")
@Primary
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("fooSqlSessionFactory") SqlSessionFactory sessionFactory) {
log.info("sessionFactory:{}", sessionFactory.toString());
return new SqlSessionTemplate(sessionFactory);
}
/**
* 返回 foo 对应的数据库事务
*
* @param fooDatasource
* @return
*/
@Bean
@Primary
public DataSourceTransactionManager fooTxManager(@Qualifier("fooDataSource") DataSource fooDatasource) {
return new DataSourceTransactionManager(fooDatasource);
}
//=========foo 数据源相关配置 end==================================================
}
- 配置类文件使用@MapperScan指定mybatis的接口定义包
- 首先使用
@ConfigurationProperties("datasource.foo")
来装载yml文件例的配置信息 - 创建
DataSource
,这里调用第一步获取配置信息。如果不在yml文件例进行配置,也可以全部在这里使用Java代码实现 - 创建SqlSessionFactory
- 创建SqlSessionTemplate
- 创建DataSourceTransactionManager
建议将需要的都创建好,这样方便做一些优化,比如超时时间、最大连接数、事务的处理方式
bar数据源的配置
bar数据源的配置和foo的模式是一样的,代码如下:
package com.lucky.spring.config;
import lombok.extern.slf4j.Slf4j;
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.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 javax.annotation.Resource;
import javax.sql.DataSource;
/**
* Created by zhangdd on 2020/7/13
*/
@Configuration
@Slf4j
@MapperScan(basePackages = "com.lucky.spring.dao.bar", sqlSessionFactoryRef = "barSqlSessionFactory")
public class BarDataSourceConfig {
static final String MAPPER_LOCATION = "classpath:mapper/bar/*.xml";
//=========bar 数据源相关配置 start==================================================
@Bean
@ConfigurationProperties("datasource.bar")
public DataSourceProperties barDataSourceProperties() {
return new DataSourceProperties();
}
/**
* 得到数据源
*
* @return
*/
@Bean("barDataSource")
public DataSource barDataSource() {
DataSourceProperties dataSourceProperties = barDataSourceProperties();
log.info("bar datasource url:{}", dataSourceProperties.getUrl());
log.info("{}", dataSourceProperties.getType().getName());
return dataSourceProperties.initializeDataSourceBuilder().build();
}
/**
*
* @param dataSource
* @return
* @throws Exception
*/
@Bean("barSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("barDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
return bean.getObject();
}
/**
* bar 对应的数据库会话模版
*
* @param sessionFactory
* @return
*/
@Bean("barSqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("barSqlSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
@Bean
@Resource
public DataSourceTransactionManager barTxManager(@Qualifier("barDataSource") DataSource barDatasource) {
return new DataSourceTransactionManager(barDatasource);
}
//=========bar 数据源相关配置 end==================================================
}
测试场景
两个数据源foo和bar连接的数据库分别是readinglist和shiro,分别以readinglist数据库例的foo表和tpermission表为例。
@RestController
public class MultiDataController {
@Autowired
TPermissionMapper permissionMapper;
@Autowired
FooMapper fooMapper;
@GetMapping("/api/getData")
public MultiDataVo getData() {
List<TPermission> tPermissions = permissionMapper.queryList();
List<Foo> foos = fooMapper.queryList();
MultiDataVo dataVo = new MultiDataVo();
dataVo.setFoos(foos);
dataVo.setPermissions(tPermissions);
return dataVo;
}
}
package com.lucky.spring.vo;
import com.lucky.spring.model.bar.TPermission;
import com.lucky.spring.model.foo.Foo;
import lombok.Data;
import java.util.List;
/**
* Created by zhangdd on 2020/7/14
*/
@Data
public class MultiDataVo {
private List<TPermission> permissions;
private List<Foo> foos;
}