SpringBoot + MyBatis使用多数据源

SpringBoot + MyBatis使用多数据源

有时候我们需要在一个项目里面集成一个或者多个数据源。

实现的思想

  • 使用mybatis持久层框架
  • mybatis的运行需要依赖于几个组件
    • DataSource 数据源
    • DataSourceTransactionManager 事务管理器
    • SqlSessionFactory SqlSession工厂,负责创建SqlSession
    • SqlSessionTemplate SqlSession,负责执行crud
  • 多数据源,就是使用多个数据源,多个事务管理器,多个SqlSession工厂,就有不同的SqlSession

Maven

使用Druid作为数据源

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.0.0</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.14</version>
		</dependency>
	</dependencies>

application.yml

配置多个数据源,这里仅仅配置了基本必须的属性

logging:
  level:
    root: debug

datasource:
  1:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=GMT%2b8
    username: root
    password: root
  
  2:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=GMT%2b8
    username: root
    password: root

多个数据源的@Configuration 配置

第一个数据源以及持久层需要的组件

package io.springboot.multidatasource.configuration;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
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.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;
@Configuration
public class DataSourceConfiguration1 {
	
	@Bean(name = "dataSource1")
	@Primary
	@ConfigurationProperties(prefix = "datasource.1")
	public DataSource dataSource() {
		return new DruidDataSource();
	}
	
	@Bean(name = "dataSourceTransactionManager1")
	@Primary
	public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dataSource1")DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}
	
	@Bean(name = "sqlSessionFactory1")
	@Primary
	public SqlSessionFactory sessionFactory(@Qualifier("dataSource1")DataSource dataSource) throws Exception {
		SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
		sessionFactoryBean.setDataSource(dataSource);
		return sessionFactoryBean.getObject();
	}
	
	@Bean("sqlSessionTemplate1")
	@Primary
	public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

第二个数据源以及持久层需要的组件

package io.springboot.multidatasource.configuration;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
public class DataSourceConfiguration2 {
	@Bean(name = "dataSource2")
	@ConfigurationProperties(prefix = "datasource.2")
	public DataSource dataSource() {
		return new DruidDataSource();
	}
	
	@Bean(name = "dataSourceTransactionManager2")
	public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dataSource2")DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}
	
	@Bean(name = "sqlSessionFactory2")
	public SqlSessionFactory sessionFactory(@Qualifier("dataSource2")DataSource dataSource) throws Exception {
		SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
		sessionFactoryBean.setDataSource(dataSource);
		return sessionFactoryBean.getObject();
	}
	
	@Bean("sqlSessionTemplate2")
	public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

可以看到,配置1中的bean,比配置2中的bean多了一个 @Primary 注解。
当其他的组件通过@Autowired等方式注入一个类的时候。而IOC中有多个该的实现。那么标注了@Primary 注解的Bean会优先注入。详细可以参阅官方文档

https://docs.spring.io/spring/docs/5.1.5.RELEASE/spring-framework-reference/core.html#beans-autowired-annotation-primary

Main

package io.springboot.multidatasource;


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

@SpringBootApplication
@MapperScans(value = {
	@MapperScan(basePackages = "io.springboot.multidatasource.mapper1",
			sqlSessionFactoryRef = "sqlSessionFactory1",sqlSessionTemplateRef = "sqlSessionTemplate1"),
	
	@MapperScan(basePackages = "io.springboot.multidatasource.mapper2",
			sqlSessionFactoryRef = "sqlSessionFactory2",sqlSessionTemplateRef = "sqlSessionTemplate2"),
})
public class MultidatasourceApplication {
	public static void main(String[] args) {
		SpringApplication.run(MultidatasourceApplication.class, args);
	}
}

重点在于 @MapperScan注解。该注解会去扫描指定包下的接口。并且动态的生成实现类。
通过 sqlSessionFactoryRef 和 sqlSessionTemplateRef 指定它们生成代理对象时。使用的SqlSessionFactory 和 SqlSession。(值就是定义在了IOC中的bean名称)。

源码

2 Likes

这里面我遇到一个问题就是配置多数据源的时候,在配置文件中配置多个mapper.xml无法加载进去,一定要在数据源的工厂里面加上bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“classpath:mapper/*.xml”));才可以。按照道理springboot的配置不是应该一致么

我也不知道你啥情况,我给你看看我的配置吧

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations:
    - classpath*:mapper/**/*-mapper.xml
    - classpath*:mapper/**/*-mapper-ext.xml

classpath:/mapper 只能检索当前工程类路径下的 mapper 文件夹
classpath*:/mapper 可以检索classpath下所有jar目录下的 mapper 文件夹


多数据源貌似不能这样加载mapper.xml需要这样才行

附上项目结构

2 Likes

我也是按博主这样配然后同样的问题用你的方法解决了 感谢分享!

1 Like

请问推不推荐使用MyBatis Generator?

我在使用generator的过程中,遇到配置多数据源的情况有个问题就是,在generatorConfig.xml中有没有什么办法能同时配置多个数据源?我现在的处理方法是先配置一个数据源,修改好对应的targetPackage路径,生成对应的文件,之后手动再修改数据源和路径,再生成另一个数据源的mapper、model这些。

 <!--配置数据库连接-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mall"
                        userId="root"
                        password="root">
            <!--解决mysql驱动升级到8.0后不生成指定数据库代码的问题-->
            <property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>

MyBatis Generator 如果没有专门提供这种针对多数据源的生成API。那你就知道一个个的生成后,去修改配置了。

Just some advice:

  1. Delete @Primary and @Qualifier, and you’ll find it works fine. Both are not necessary .
  2. Delete the property “name=…” in @Bean(name = "dataSource2") , for it can be generated automaticlly, see the javadoc about @Bean, about name() , it comments " If left unspecified, the name of the bean is the name of the annotated method. "
1 Like