環境背景
這里以配置兩個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;
}