mybatis 配置多数据源

使用mybatis配置多数据源我接触过的有两种方式,一种是通过java config的方式手动配置两个数据源,另一种方式便是使用mybatis-plus-dynamic。*

总体来说,配置主要包括,产生DataSource,然后是mybatis所需要的SqlSessionFactory,以及配置相应的事务管理器

  • pom
<!--MyBatis整合SpringBoot框架的起步依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <!-- Mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <!-- jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.7.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
  • 配置文件 yml
server:
  port: 8080

spring:
  datasource:
    master:
      jdbc-url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave:
      jdbc-url: jdbc:mysql://43.143.217.124:3306/hongbei?characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
  • java config 配置数据源一
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.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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;

/**
 * @Classname DB1DataSourceConfig
 * @Description DB1DataSourceConfig
 * @Date 2023-02-24 15:14
 * @Created by lihw
 */
@Configuration
@MapperScan(
        basePackages = "com.example.demo.mapper.master",
        sqlSessionFactoryRef = "masterSqlSessionFactory")
public class DB1DataSourceConfig {

    String MAPPER_LOCATION = "classpath*:abc/*.xml";

    @Primary
    @Bean("masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource getMasterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean("masterSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        // 使用 mybatis plus  配置
        //MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        //mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        //mybatisSqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
        //        .getResources(MAPPER_LOCATION)); // "classpath:mapping/*Mapper.xml"
        //mybatisSqlSessionFactoryBean.setTypeAliasesPackage("com.example.demo.entity");
        //
        //return mybatisSqlSessionFactoryBean.getObject();

        // mybatis 配置
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
              .getResources(MAPPER_LOCATION));
        sqlSessionFactoryBean.setTypeAliasesPackage("com.example.demo.entity.master");

        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);

        sqlSessionFactoryBean.setConfiguration(configuration);

        return sqlSessionFactoryBean.getObject();
    }

    @Primary
    @Bean("masterSqlSessionTemplate")
    public SqlSessionTemplate  sqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
        return sqlSessionTemplate;
    }

    @Bean("masterTransactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
        return dataSourceTransactionManager;
    }


}
  • 配置数据源 二
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.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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.sql.DataSource;

/**
 * @Classname DB1DataSourceConfig
 * @Description DB1DataSourceConfig
 * @Date 2023-02-24 15:14
 * @Created by lihw
 */
@Configuration
@MapperScan(
        basePackages = "com.example.demo.mapper.slave",
        sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class DB2DataSourceConfig {

    String MAPPER_LOCATION = "classpath*:slave/*.xml";

    //@Primary
    @Bean("slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource getSlaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    //@Primary
    @Bean("slaveSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        // mybatis plus配置
        //MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        //mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        //mybatisSqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
        //        .getResources(MAPPER_LOCATION)); // "classpath:mapping/*Mapper.xml"
        //mybatisSqlSessionFactoryBean.setTypeAliasesPackage("com.example.demo.entity");
        //
        //return mybatisSqlSessionFactoryBean.getObject();


        // mybatis 配置
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(MAPPER_LOCATION));
        sqlSessionFactoryBean.setTypeAliasesPackage("com.example.demo.entity.slave");

        // 设置mybatis配置
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 下划线转驼峰
        configuration.setMapUnderscoreToCamelCase(true);

        sqlSessionFactoryBean.setConfiguration(configuration);

        return sqlSessionFactoryBean.getObject();
    }

    //@Primary
    @Bean("slaveSqlSessionTemplate")
    public SqlSessionTemplate  sqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
        return sqlSessionTemplate;
    }

    @Bean("slaveTransactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
        return dataSourceTransactionManager;
    }


}
  • 目录结构
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K95yKTJe-1677467535741)(https://note.youdao.com/yws/res/4010/WEBRESOURCE3a7424410d156d5dc0d6bd00cd7bf638 “image.png”)]
  • 测试接口
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    StudentMapper studentMapper;

    @Autowired
    SysUserMapper sysUserMapper;
	// 数据源一 查询
    @GetMapping("list")
    public List<Student> getUserList(){

        List<Student> userList = studentMapper.getUserList();
        System.out.println(userList);

        return userList;
    }
	// 数据源二 查询
    @GetMapping("msg2")
    public String getmsg2(){

        List<SysUser> user = sysUserMapper.getUserList();
        System.out.println(user);

        return "msg22";
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSCJmOj9-1677467535743)(https://note.youdao.com/yws/res/4016/WEBRESOURCE0cc80d06ca0c483d00bb9e34f16ef10d)]

  • pom
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.1.1</version>
</dependency>
  • yml
# mybatis-plus-dynamic 配置多数据源
spring:
  datasource:
    dynamic:
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql://43.143.217.124:3306/hongbei?characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
      # 指定主数据库
      primary: master

#mybatis:
#  mapper-locations: classpath*:abc/*.xml,classpath*:slave/*.xml

mybatis-plus:
  mapper-locations: classpath*:abc/*.xml,classpath*:slave/*.xml
  • 使用案例
@Service
@DS("slave-1")
public class TbServiceImpl extends ServiceImpl<TbDao, TbBean> implements TbService {
    @Override
    public String save1() {
        TbBean tbBean = new TbBean();
        tbBean.setName("王五");
        tbBean.setSubject("英语");
        tbBean.setScore(113);
        this.save(tbBean);
        return "success";
    }
}

此处是模拟的一个新增操作,注意类上面的@DS注解,该注解可以标注在类或方法上面;也可以标注在Mapper接口上面,但是不建议同时在Mapper和service上同时标注,可能会出现问题。该注解的value属性便是对应于在yaml中配置的数据源名称,如果没有给值,默认就是使用数据源名为master的数据源。

  • 踩坑日记:

如下代码:

@Service
@DS("master")
public class UserServiceImpl extends ServiceImpl<UserDao, UserBean> implements UserService {
 
    @Autowired
    private TbService tbService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add() {
        UserBean userBean = new UserBean();
        userBean.setId(3);
        userBean.setLoginName("zhangsan");
        userBean.setName("张三");
        userBean.setPassword("123456");
        this.save(userBean);
		// 第二个数据源
        tbService.save1();
    }
}

意思就是我想在保存userBean时同时调用一下tbService的save1方法,注意tbService被@DS(“slave-1”)注解标注,它对应于sqlServer数据库的操作。当直接调用上面的add方法时,会报如下的错误:

springboot mysql8 数据源 springboot多数据源mybatis_java

反正就是死活找不到tb这张表,实际上tb这张表是确实存在于sqlServer数据库中的,之所以报错是由于加事务的原因@Transactional(rollbackFor = Exception.class),由于spring事务默认的传播级别是:

Propagation.REQUIRED

这个事务的特性就是如果上级方法调用时已经获取了事务,则该方法内调用的其它事务方法将复用同一个事务,结果就是对userBean的操作是对应于mysql的,由于加了事务,所以tbService.save1()方法还是在该事务内,造成的结果就是会在mysql数据库中找tb这张表,肯定找不到,结果就报错了,解决方式如下:

@Service
@DS("slave-1")
public class TbServiceImpl extends ServiceImpl<TbDao, TbBean> implements TbService {
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public String save1() {
        TbBean tbBean = new TbBean();
        tbBean.setName("王五");
        tbBean.setSubject("英语");
        tbBean.setScore(113);
        this.save(tbBean);
        return "success";
    }
}

给save1()方法加上  @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) , 使其在每次获取事务时都是重新产生一个,不再复用上级方法的事务。

  • pom
<!-- Druid 数据连接池依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>
       <!--MyBatis整合SpringBoot框架的起步依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <!-- jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.7.8</version>
        </dependency>
        <!-- Mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
  • yml
# druid 多数据源配置
master:
  datasource:
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

slave:
  datasource:
    url: jdbc:mysql://43.143.217.124:3306/hongbei?characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  • 数据源一 配置
import com.alibaba.druid.pool.DruidDataSource;
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 javax.sql.DataSource;



/**
 * @Classname DruidConfig
 * @Description DruidConfig
 * @Date 2023-02-27 10:45
 * @Created by lihw
 */
@Configuration
@MapperScan(basePackages = {DruidConfig.PACKAGE},
        sqlSessionFactoryRef = "masterSqlSessionFactory")
public class DruidConfig {

    // 精确到 master 目录,以便跟其他数据源隔离
    static final String PACKAGE = "com.example.demo.mapper.master";
    static final String MAPPER_LOCATION = "classpath:abc/**/*.xml";

    @Value("${master.datasource.url}")
    private String url;

    @Value("${master.datasource.username}")
    private String user;

    @Value("${master.datasource.password}")
    private String password;

    @Value("${master.datasource.driver-class-name}")
    private String driverClass;

    @Bean("masterDataSource")
    @Primary
    public DataSource masterDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClass);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(user);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

    @Bean("masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager(){
        return new DataSourceTransactionManager(masterDataSource());
    }

    @Bean("masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(MAPPER_LOCATION));
        // mybatis 配置
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 驼峰下划线
        configuration.setMapUnderscoreToCamelCase(true);
        sqlSessionFactoryBean.setConfiguration(configuration);

        return sqlSessionFactoryBean.getObject();
    }

}
  • 数据源二 配置
import com.alibaba.druid.pool.DruidDataSource;
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 javax.sql.DataSource;


/**
 * @Classname DruidConfig
 * @Description DruidConfig
 * @Date 2023-02-27 10:45
 * @Created by lihw
 */
@Configuration
@MapperScan(basePackages = {DruidConfig2.PACKAGE},
        sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class DruidConfig2 {

    // 精确到 master 目录,以便跟其他数据源隔离
    static final String PACKAGE = "com.example.demo.mapper.slave";
    static final String MAPPER_LOCATION = "classpath:slave/**/*.xml";

    @Value("${slave.datasource.url}")
    private String url;

    @Value("${slave.datasource.username}")
    private String user;

    @Value("${slave.datasource.password}")
    private String password;

    @Value("${slave.datasource.driver-class-name}")
    private String driverClass;

    @Bean("slaveDataSource")
    @Primary
    public DataSource slaveDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClass);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(user);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

    @Bean("slaveTransactionManager")
    @Primary
    public DataSourceTransactionManager slaveTransactionManager(){
        return new DataSourceTransactionManager(slaveDataSource());
    }

    @Bean("slaveSqlSessionFactory")
    @Primary
    public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(MAPPER_LOCATION));
        // mybatis 配置
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 驼峰下划线
        configuration.setMapUnderscoreToCamelCase(true);
        sqlSessionFactoryBean.setConfiguration(configuration);

        return sqlSessionFactoryBean.getObject();
    }

}
  • 测试代码
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    StudentMapper studentMapper;

    @Autowired
    SysUserMapper sysUserMapper;

    @GetMapping("list")
    public List<Student> getUserList(){

        List<Student> userList = studentMapper.getUserList();
        System.out.println(userList);

        return userList;
    }

    @GetMapping("msg2")
    public String getmsg2(){

        List<SysUser> user = sysUserMapper.getUserList();
        System.out.println(user);

        return "msg22";
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VnOmBExy-1677467535745)(https://note.youdao.com/yws/res/4051/WEBRESOURCE1a8c5b356140fb73fc973a659243a302)]