springboot下多数据源

目的:

需要能够动态切换数据源,实现对不同数据库的增删查改操作

  • 需要引入的jar包,pom.xml如下:
<?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 http://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.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lan</groupId>
    <artifactId>springboot-druid</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>springboot-druid</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>

        <!--druid 连接池-->
        <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>
            <version>1.18.6</version>
        </dependency>

        <!--aop-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  • 在项目启动类上配置,取消spring自动配置数据源操作
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class SpringbootDruidApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDruidApplication.class, args);
    }

}
  • 在配置文件application.yml中配置数据库信息等
server:
  port: 9696
mybatis:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

# 控制台打印sql
logging:
  level:
    com.lan.springbootdruid.mapper: debug

spring:
  datasource:
    druid:
      master:
        url: jdbc:mysql://localhost:3306/springbootdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8
        username: root
        password: 12345
        initialSize: 5
      other:
        url: jdbc:mysql://localhost:3306/springbootdb_cluster?useSSL=false&useUnicode=true&characterEncoding=UTF-8
        username: root
        password: 12345
      driver-class-name: com.mysql.jdbc.Driver
      # 初始化数量
      initial-size: 5
      # 最大活跃数
      max-active: 20
      # 最大连接等待超时时间
      max-wait: 60000
      # 最小
      min-idle: 5
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      # 打开PSCache
      pool-prepared-statements: true
      # 指定每个连接上PSCache的大小
      max-pool-prepared-statement-per-connection-size: 20
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,‘wall’用于防火墙
      filters: stat,wall,log4j
      # 通过conncetProperties属性来打开mergeSql功能,慢sql记录
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
  • 创建数据源枚举类
public enum DataSourceKey {

    DB_MASTER,
    DB_OTHER;
}
  • 创建DynamicDataSourceContextHolder类,来解决多线程访问全局变量的问题
package com.lan.springbootdruid.datasource;

import com.lan.springbootdruid.enums.DataSourceKey;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: Lan
 * @date: 2019/5/5 17:52
 * @description:解决多线程访问全局变量的问题
 */
@Slf4j
public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<DataSourceKey> currentDateSource = new ThreadLocal<>();

    /**
     * 清除当前数据源
     */
    public static void clear() {
        log.info("清除当前数据源");
        currentDateSource.remove();
    }

    /**
     * 获取当前使用的数据源
     *
     * @return
     */
    public static DataSourceKey get() {
        log.info("获取当前使用的数据源:{}", currentDateSource.get());
        return currentDateSource.get();
    }

    /**
     * 设置当前使用数据源
     *
     * @param key 需要设置的数据源
     */
    public static void set(DataSourceKey key) {
        log.info("设置当前使用数据源:{}", key);
        currentDateSource.set(key);
    }
}
  • 设置数据源
package com.lan.springbootdruid.datasource;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @author: Lan
 * @date: 2019/5/5 17:57
 * @description:
 */
@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    /**
     * 设置数据源
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        log.info("当前数据源:{}", DynamicDataSourceContextHolder.get());
        return DynamicDataSourceContextHolder.get();
    }
}
  • 配置数据源
package com.lan.springbootdruid.datasource;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.lan.springbootdruid.enums.DataSourceKey;
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.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 org.springframework.transaction.PlatformTransactionManager;

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

/**
 * @author: Lan
 * @date: 2019/5/5 18:01
 * @description:配置数据源
 */
@MapperScan(basePackages = "com.lan.springbootdruid.mapper")
@Configuration
public class DynamicDataSourceConfiguration {

    /**
     * 数据源
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid.master")
    public DataSource dbMaster() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 数据源
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid.other")
    public DataSource dbOther() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 配置项(数据库与实体类映射,驼峰命名)
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "mybatis.configuration")
    public org.apache.ibatis.session.Configuration configuration() {
        return new org.apache.ibatis.session.Configuration();
    }

    /**
     * 核心动态数据源
     *
     * @return
     */
    @Bean
    public DataSource dynamicDataSource() {
        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
        dynamicRoutingDataSource.setDefaultTargetDataSource(dbMaster());
        Map<Object, Object> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put(DataSourceKey.DB_MASTER, dbMaster());
        dataSourceMap.put(DataSourceKey.DB_OTHER, dbOther());
        dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
        return dynamicRoutingDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //开启数据库与实体类映射
        sqlSessionFactoryBean.setConfiguration(configuration());
        sqlSessionFactoryBean.setDataSource(dynamicDataSource());
        //此处设置为了解决找不到mapper文件的问题
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate() throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory());
    }

    /**
     * 事务管理
     *
     * @return
     */
    @Bean
    public PlatformTransactionManager platformTransactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }
}
  • 通过aop来动态切换数据源
package com.lan.springbootdruid.datasource;

import com.lan.springbootdruid.enums.DataSourceKey;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author: Lan
 * @date: 2019/5/5 18:22
 * @description:数据源切面
 */
@Aspect
@Order(-1)
@Component
@Slf4j
public class DynamicDataSourceAspect {

//    @Pointcut("execution(* com.lan.springbootdruid.controller..*.*(..))")
//    public void pointCut() {
//
//    }

    @Pointcut("@annotation(TargetDataSource)")
    public void pointCut() {

    }

    /**
     * 执行前更换数据源
     *
     * @param joinPoint
     */
    @Before("@annotation(TargetDataSource)")
    public void doBefore(JoinPoint joinPoint) {
        log.info("执行前");
        //获取类上的注解
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        TargetDataSource targetDataSource = method.getAnnotation(TargetDataSource.class);
        if (targetDataSource == null) {
            log.info("使用默认数据源:{}", DataSourceKey.DB_MASTER);
            DynamicDataSourceContextHolder.set(DataSourceKey.DB_MASTER);
        } else {
            log.info("设置数据源:{}", targetDataSource.dataSourceKey());
            DynamicDataSourceContextHolder.set(targetDataSource.dataSourceKey());
        }
    }

    /**
     * 执行后清除数据源
     *
     * @param joinPoint
     */
    @After("@annotation(TargetDataSource)")
    public void doAfter(JoinPoint joinPoint) {
        log.info("执行后");
        //获取类上的注解
        TargetDataSource targetDataSource = joinPoint.getTarget().getClass().getAnnotation(TargetDataSource.class);
        if (targetDataSource == null) {
            log.info("当前数据源:{}", DataSourceKey.DB_MASTER);
        } else {
            log.info("当前数据源:{}", targetDataSource.dataSourceKey());
        }
        DynamicDataSourceContextHolder.clear();
    }
}
  • mapper
package com.lan.springbootdruid.mapper;

import com.lan.springbootdruid.datasource.TargetDataSource;
import com.lan.springbootdruid.entity.User;
import com.lan.springbootdruid.enums.DataSourceKey;
import org.springframework.stereotype.Repository;

/**
 * @author: Lan
 * @date: 2019/5/5 18:26
 * @description:
 */
@Repository
public interface UserMapper {

    /**
     * 通过ID获取用户对象
     *
     * @param id
     * @return
     */
    @TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)
    User getUserById(int id);

    /**
     * 通过id修改用户名
     *
     * @param id
     * @return
     */
    @TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)
    int updateUserName(int id);
}
  • controller
package com.lan.springbootdruid.controller;

import com.lan.springbootdruid.entity.City;
import com.lan.springbootdruid.entity.User;
import com.lan.springbootdruid.service.CityService;
import com.lan.springbootdruid.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * @author: Lan
 * @date: 2019/5/5 18:42
 * @description:
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private CityService cityService;

    @GetMapping("/{id}")
    public Map<String, Object> getUserById(@PathVariable("id") int id) {
        User user = userService.getUserById(id);
        City city = cityService.getCityById(id);
        Map<String, Object> map = new HashMap<>(2);
        map.put("user", user);
        map.put("city", city);
        return map;
    }
}

git地址:https://gitee.com/lanran1/multiple_datasources

事务还未解决 !!!

猜你喜欢

转载自blog.csdn.net/qq_35494808/article/details/89888918
今日推荐