springboot mybtis 多数据源动态切换

注释:本例使用springboot+mybatis+通用mapper实现多数据源动态切换;@Transactional只能控制默认数据源的事物,同一个方法中使用多个数据源时,如需要控制事物请使用分布式事物

其中事物只能控制默认数据源,如需使用事物控制需使用分布式事物

本文贴出demo关键代码,结尾附上demo地址

  1. 创建三个库multiple-data-source-a,multiple-data-source-b,multiple-data-source-c

multiple-data-source-a库中创建user-tab表

CREATE TABLE `user_tab` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_number` varchar(50) DEFAULT NULL COMMENT '用户编号',
  `nick_name` varchar(20) DEFAULT NULL COMMENT '昵称',
  `user_name` varchar(20) DEFAULT NULL COMMENT '账号',
  `password` varchar(30) DEFAULT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';

multiple-data-source-b库中创建goods_tab表

CREATE TABLE `goods_tab` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_number` varchar(50) DEFAULT NULL COMMENT '编号',
  `goods_name` varchar(20) DEFAULT NULL COMMENT '名称',
  `goods_price` double(5,2) DEFAULT NULL COMMENT '价格',
  `goods_num` int(8) DEFAULT NULL COMMENT '商品数量',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='商品表';

multiple-data-source-c库中创建order_tab表

CREATE TABLE `order_tab` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_number` varchar(50) DEFAULT NULL COMMENT '订单编号',
  `goods_number` varchar(50) DEFAULT NULL COMMENT '商品编号',
  `user_number` varchar(50) DEFAULT NULL COMMENT '用户编号',
  `pay_money` double(5,2) DEFAULT NULL COMMENT '支付金额',
  `buy_count` int(5) DEFAULT NULL COMMENT '购买数量',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='订单表';
  1. 创建一个springboot项目,笔者boot版本是1.5.2
    这是demo结构
  2. 添加如下maven依赖
		<!--ali数据库连接池-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.8</version>
		</dependency>
		
		<!--mysql 连接 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		
		<!--mybatis-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		
		<!--通用mapper-->
		<dependency>
			<groupId>tk.mybatis</groupId>
			<artifactId>mapper-spring-boot-starter</artifactId>
			<version>1.2.4</version>
		</dependency>
		
		<!--阿里巴巴json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>
  1. 此处使用的yml方式配置端口,数据库连接(使用的druid),mybatis配置
server:
  port: 8088
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    # 用户
    user:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/multiple-data-source-a?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
      username: root
      password: 123456
    # 商品
    goods:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/multiple-data-source-b?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
      username: root
      password: 123456
    # 订单
    order:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://118.24.151.228:3306/multiple-data-source-c?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
      username: root
      password: 123456  
   
    maximum-pool-size: 100
    max-idle: 10
    max-wait: 10000
    min-idle: 5
    initial-size: 5
    validation-query: SELECT 1
    test-on-borrow: false
    test-while-idle: true
    time-between-eviction-runs-millis: 18800


#mybatis配置
mybatis:
  mapper-locations: classpath:mybatis/*Mapper.xml
  type-aliases-package: com.multipledatasource.web.bean
  1. 接下来是多数据源配置;以上4步如有不清楚的请参文末demo
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 创建数据源
 */
@Configuration
@MapperScan(basePackages = "com.multipledatasource.web.mapper")//扫描包路径下的mapper
public class DataSourceConfig {

    /**
     * 读取yml或者properties配置文件
     */
    @Autowired
    private Environment env;

    /**
     * 配置数据源
     * @return
     */
    //用户
    @Bean(name = "dataSourceUser")//指定名称,未指定默认为方法名
    @ConfigurationProperties(prefix = "spring.datasource.user")//prefix对应yml中的前缀
    public DataSource dataSourceUser(){
        return DataSourceBuilder.create().build();
    }
    //商品
    @Bean(name = "dataSourceGoods")
    @ConfigurationProperties(prefix = "spring.datasource.goods")
    public DataSource dataSourceGoods(){
        return DataSourceBuilder.create().build();
    }
    //订单
    @Bean(name = "dataSourceOrder")
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DataSource dataSourceOrder(){
        return DataSourceBuilder.create().build();
    }

    /**
     * 动态切换数据源类
     * @Primary 指定默认数据源
     * @param user
     * @param goods
     * @param order
     * @return
     */
    @Primary //指定默认数据源,此处为第一个是dataSourceUser
    @Bean("dataSource")
    public DynamicDataSource dataSource(@Qualifier("dataSourceUser") DataSource user,
                                        @Qualifier("dataSourceGoods") DataSource goods,
                                        @Qualifier("dataSourceOrder") DataSource order) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnum.USER, user);
        targetDataSources.put(DataSourceEnum.GOODS, goods);
        targetDataSources.put(DataSourceEnum.ORDER, order);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        // 设置默认的数据源
        dynamicDataSource.setDefaultTargetDataSource(user);
        return dynamicDataSource;

    }

    /**
     * 根据动态数据源配置,创建sqlSessionFactory
     * @param user
     * @param goods
     * @param order
     * @return
     * @throws Exception
     */
    @Primary
    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSourceUser") DataSource user,
                                               @Qualifier("dataSourceGoods") DataSource goods,
                                               @Qualifier("dataSourceOrder") DataSource order) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(this.dataSource(user, goods,order));
        sessionFactory.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));//对应yml中mybatis的bean目录
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations")));//对应yml中mybatis的xml
        return sessionFactory.getObject();

    }


    /**
     * 根据动态数据源配置,创建transactionManager
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean("transactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DynamicDataSource dataSource) throws Exception {
        return new DataSourceTransactionManager(dataSource);
    }


}

/**
 *   数据源类型枚举类
 */
public enum DataSourceEnum {
    USER,GOODS,ORDER
}

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态切换数据源类
 *      使用DatabaseContextHolder获取当前线程的DataSourceEnum
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }

}
/**
 *数据源切换类
 *      构建一个DataSourceEnum容器,并提供了向其中设置和获取DataSorceEnum的方法
 */
public class DataSourceContextHolder {

    private static final ThreadLocal<DataSourceEnum> contextHolder = new ThreadLocal<DataSourceEnum>(){
        @Override
        protected DataSourceEnum initialValue() {
            return DataSourceEnum.USER;//初始为用户数据源
        }
    };

    public static void setDatabaseType(DataSourceEnum type){
        contextHolder.set(type);
    }
    public static DataSourceEnum getDataSourceType(){
        return contextHolder.get();
    }
    public static void clearDataSourceType(){
        contextHolder.remove();
    }
}
  1. 测试,通过DataSourceContextHolder.setDatabaseType(DataSourceEnum.ORDER);设置动态切换数据源
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    OrderTabMapper orderTabMapper;
    @Autowired
    GoodsTabMapper goodsTabMapper;


    @Override
    public JSONObject addOrder(JSONObject jsonObject) {
        String goodsNumber =  jsonObject.getString("goodsNumber");
        String userNumber = jsonObject.getString("userNumber");
        Double payMoney = jsonObject.getDouble("payMoney");
        //新增订单
        OrderTab orderTab = new OrderTab();
        orderTab.setBuyCount(1);
        orderTab.setGoodsNumber(goodsNumber);
        orderTab.setOrderNumber(UUID.randomUUID().toString());
        orderTab.setPayMoney(payMoney);
        orderTab.setUserNumber(userNumber);
        DataSourceContextHolder.setDatabaseType(DataSourceEnum.ORDER);//设置订单数据源
        orderTabMapper.addOrder(orderTab);

        //商品数量减一
        DataSourceContextHolder.setDatabaseType(DataSourceEnum.GOODS);//设置商品数据源
        int identify = goodsTabMapper.goodsDecrement(goodsNumber);
        JSONObject json = new JSONObject();
        json.put("code",1);
        json.put("msg","成功");
        return json;
    }
}

postman示例
本文大多为代码,其中的注解与关键类并未做释义,仅仅实现了功能,想知其所以然请自行百度或者评论,项目demo地址:https://gitee.com/kylin-easy/multiple-data-source

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kylin_essay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值