Spring Boot 配置动态数据源

Spring Boot 配置动态数据源

Spring Framework 为 SQL 数据库提供了广泛的支持。从直接使用 JdbcTemplate 进行 JDBC 访问到完 全的对象关系映射(object relational mapping)技术,比如 Hibernate。Spring Data 提供了更多级 别的功能,直接从接口创建的 Repository 实现,并使用了约定从方法名生成查询。

目录

Spring Boot 配置动态数据源

一、默认数据源HikariDataSource

1、导入pom依赖

2、配置yaml

3、执行crud操作

二、自定义数据源DruidDataSource

1、添加druid的maven配置

2、yaml配置

3、Configurations 配置

三、springboot配置多数据源并动态切换

1、导入aop的maven配置

2、修改配置文件类

3、创建数据源枚举类

4、数据源动态切换处理

5、创建动态数据源

6、注入数据源

7、自定义多数据源切换注解,AOP注解拦截

8.使用切换数据源注解

9、解决项目启动循环依赖问题


一、默认数据源HikariDataSource

1、导入pom依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

2、配置yaml

spring:
  datasource:
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://127.0.0.1:3306/ds1?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=false # springboot2.0数据源配置使用 2.0以下使用url
      username: root
      password: ******
      type: com.alibaba.druid.pool.DruidDataSource

3、执行crud操作

1、有了数据源(com.zaxxer.hikari.HikariDataSource),然后可以拿到数据库连接 (java.sql.Connection),有了连接,就可以使用连接和原生的 JDBC 语句来操作数据库 
2、即使不使用第三方第数据库操作框架,如 MyBatis等,Spring 本身也对原生的JDBC 做了轻量级的封 装,即 org.springframework.jdbc.core.JdbcTemplate。 
3、数据库操作的所有 CRUD 方法都在 JdbcTemplate 中。 
4、Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序 员只需自己注入即可使用 
5、JdbcTemplate 的自动配置原理是依赖 org.springframework.boot.autoconfigure.jdbc 包下的 org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration 类

public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    public List<Map<String, Object>> userList() {
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
}

二、自定义数据源DruidDataSource

1、添加druid的maven配置

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- 配置filters log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

2、yaml配置

spring:
  datasource:
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://127.0.0.1:3306/ds1?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=false
      username: root
      password: ******
      type: com.alibaba.druid.pool.DruidDataSource
      #Spring Boot 默认是不注入这些属性值的,需要自己绑定
      #druid 数据源专有配置
      initialSize: 5
      minIdle: 5
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
      #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
      #测试类,发现配置的参数没有生效
      #需要定义druidDatasource的配置类,绑定参数
      #则导入 log4j 依赖即可,Maven 地址:
      #https://mvnrepository.com/artifact/log4j/log4j
      filters: stat,wall,log4j
      maxPoolPreparedStatementPerConnectionSize: 20
      useGlobalDataSourceStat: true
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3、Configurations 配置

@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean //方法注入(外部化配置)
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

    @Bean
    public ServletRegistrationBean druidServletRegistrationBean() {
        ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        Map<String, String> initParams = new HashMap<>();
        initParams.put("loginUsername", "admin");
        initParams.put("loginPassword", "123456");
        //后台允许谁可以访问
        //initParams.put("allow", "localhost"):表示只有本机可以访问
        //initParams.put("allow", ""):为空或者为null时,表示允许所有访问
        initParams.put("allow", "");
        //deny:Druid 后台拒绝谁访问
        //initParams.put("roc", "127.0.0.1");表示禁止此ip访问

        servletRegistrationBean.setInitParameters(initParams);
        return servletRegistrationBean;
    }

    //配置 Druid 监控 之  web 监控的 filter
    //WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
    @Bean
    public FilterRegistrationBean webStatFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());

        //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
        Map<String, String> initParams = new HashMap<>();
        initParams.put("exclusions", "*.js,*.css,/druid/*");
        bean.setInitParameters(initParams);

        //"/*" 表示过滤所有请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }

}

三、springboot配置多数据源并动态切换

DataSource是和线程绑定的,动态数据源的配置主要是通过继承AbstractRoutingDataSource类实现的,实现在AbstractRoutingDataSource类中的 protected Object determineCurrentLookupKey()方 法来获取数据源,所以我们需要先创建一个多线程线程数据隔离的类来存放DataSource,然后在 determineCurrentLookupKey()方法中通过这个类获取当前线程的DataSource,在 AbstractRoutingDataSource类中,DataSource是通过Key-value的方式保存的,我们可以通过 ThreadLocal来保存Key,从而实现数据源的动态切换。

1、导入aop的maven配置

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
		</dependency>

2、修改配置文件类

spring:
  datasource:
    ds1:
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://127.0.0.1:3306/ds1?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=false # springboot2.0多数据源配置使用
      username: root
      password: ******
      type: com.alibaba.druid.pool.DruidDataSource
    ds2:
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://127.0.0.1:3306/ds2?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=false
      username: root
      password: ******
      type: com.alibaba.druid.pool.DruidDataSource
    activity:
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://127.0.0.1:3306/activity?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=false
      username: root
      password: ******
      type: com.alibaba.druid.pool.DruidDataSource

3、创建数据源枚举类

public enum DataSourceType {
    DS1,
    DS2,
    ACTIVITY
}

4、数据源动态切换处理

创建一个数据源切换处理类,有对数据源变量的获取、设置和情况的方法,其中threadlocal用于保存某 个线程共享变量

public class DynamicDataSourceContextHolder {
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>
            ();
    /**
     * 设置数据源变量
     *
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType) {
        System.out.printf("切换到{%s}数据源\n", dataSourceType);
        CONTEXT_HOLDER.set(dataSourceType);
    }
    /**
     * 获取数据源变量
     *
     * @return
     */
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

5、创建动态数据源

动态切换数据源主要依靠AbstractRoutingDataSource。创建一个AbstractRoutingDataSource的子 类,重写determineCurrentLookupKey方法,用于决定使用哪一个数据源。这里主要用到 AbstractRoutingDataSource的两个属性defaultTargetDataSource和targetDataSources。 defaultTargetDataSource默认目标数据源,targetDataSources(map类型)存放用来切换的数据 源。

public class DynamicDataSource extends AbstractRoutingDataSource {
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object,
                Object> targetDataSources) {

        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
// afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
        //super.afterPropertiesSet();
    }

    /**
     * 根据Key获取数据源的信息
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

6、注入数据源

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.ds1")
    public DataSource ds1() {
        //return DataSourceBuilder.create().build();
        return new DruidDataSource();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.ds2")
    public DataSource ds2() {
        //return DataSourceBuilder.create().build();
        return new DruidDataSource();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.activity")
    public DataSource activity() {
        //return DataSourceBuilder.create().build();
        return new DruidDataSource();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource ds1, DataSource ds2,DataSource activity) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.DS1.name(), ds1);
        targetDataSources.put(DataSourceType.DS2.name(), ds2);
        targetDataSources.put(DataSourceType.ACTIVITY.name(), activity);
        return new DynamicDataSource(ds1, targetDataSources);
    }
}

7、自定义多数据源切换注解,AOP注解拦截

设置拦截数据源的注解,可以设置在具体的类上,或者在具体的方法上
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceAnnotation {
    /**
     * 切换数据源名称
     */
    DataSourceType value() default DataSourceType.DS1;
}
AOP拦截类的实现
通过拦截上面的注解,在其执行之前处理设置当前执行SQL的数据源的信息,CONTEXT_HOLDER.set(dataSourceType)这里的数据源信息从我们设置的注解上面获取信息,如果没有设置就是用默认的数据源的信息
@Aspect
@Order(1)
@Component
public class DataSourceAspect{
    @Pointcut("@annotation(com.springboot.springboot.datasource.DataSourceAnnotation)")
    public void dsPointCut() {
        System.out.println("qierudianaaaa");
    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class);

        if (dataSourceAnnotation != null) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSourceAnnotation.value().name());
        }
        try {
            System.out.println("执行方法前");
            Object result = point.proceed();
            System.out.println("执行方法后");
            return result;
        } finally {
// 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
}

8.使用切换数据源注解

public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    //方法内多数据源时,暂不支持事务
    @DataSourceAnnotation(value = DataSourceType.DS1)
    public List<Map<String, Object>> userList() {
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        //可在方法内切换注解
        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.ACTIVITY.name());
        List<Map<String, Object>> maps2 = jdbcTemplate.queryForList(sql);
        return maps;
    }
    @DataSourceAnnotation(value = DataSourceType.ACTIVITY)
    public List<Map<String, Object>> userList2() {
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
}

9、解决项目启动循环依赖问题

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class SpringbootApplication {

	public static void main(String[] args) {
//		SpringApplication.run(SpringbootApplication.class, args);
		SpringApplication app = new SpringApplication(SpringbootApplication.class);
		app.setBannerMode(Banner.Mode.OFF);
		app.run(args);

	}

}

项目demo 链接

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Spring Boot支持动态数据源配置,可以根据需要动态地切换数据源。以下是动态数据源配置的步骤: 1. 在pom.xml文件中添加依赖: ```xml <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> ``` 2. 配置数据源,在application.properties文件中添加以下配置: ```properties spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/db?characterEncoding=UTF-8&serverTimezone=UTC spring.datasource.hikari.username=root spring.datasource.hikari.password=123456 ``` 3. 创建多个数据源,在配置类中添加以下配置: ```java @Configuration public class DataSourceConfig { @Bean(name = "dataSource1") @ConfigurationProperties(prefix = "spring.datasource.hikari.datasource1") public DataSource dataSource1() { return DataSourceBuilder.create().type(HikariDataSource.class).build(); } @Bean(name = "dataSource2") @ConfigurationProperties(prefix = "spring.datasource.hikari.datasource2") public DataSource dataSource2() { return DataSourceBuilder.create().type(HikariDataSource.class).build(); } } ``` 4. 创建动态数据源,在配置类中添加以下配置: ```java @Configuration public class DynamicDataSourceConfig { @Bean(name = "dynamicDataSource") public DynamicDataSource dynamicDataSource(@Qualifier("dataSource1") DataSource dataSource1, @Qualifier("dataSource2") DataSource dataSource2) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("dataSource1", dataSource1); targetDataSources.put("dataSource2", dataSource2); return new DynamicDataSource(dataSource1, targetDataSources); } } ``` 5. 创建动态数据源切换类,在配置类中添加以下配置: ```java public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return contextHolder.get(); } public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static void clearDataSource() { contextHolder.remove(); } } ``` 6. 在需要切换数据源的地方,调用DynamicDataSource.setDataSource("dataSource1")或DynamicDataSource.setDataSource("dataSource2")方法切换数据源。 以上就是Spring Boot动态数据源配置的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

numun

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

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

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

打赏作者

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

抵扣说明:

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

余额充值