使用springboot+druid双数据源动态配置操作

下面是“使用SpringBoot+Druid双数据源动态配置操作”的完整攻略及两条示例。

一、概述

在实际的项目开发中,经常会遇到同时操作多个不同的数据库的情况,比如读写分离、多租户等。使用SpringBoot+Druid双数据源动态配置操作,可以有效地解决这些问题。

二、配置SpringBoot+Druid

1. 引入相关依赖

pom.xml 文件中加入以下依赖:

<!-- SpringBoot依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- MySQL驱动依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Druid连接池依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>${druid.version}</version>
</dependency>
<!-- MyBatis依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>${mybatis.version}</version>
</dependency>

2. 配置Druid连接池

application.yml 文件中配置Druid连接池:

spring:
  datasource:
    # 主数据源
    druid:
      url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      initial-size: 1
      max-active: 20
      min-idle: 1
      max-wait: 60000
      time-between-eviction-runs-millis: 10000
      min-evictable-idle-time-millis: 300000
    # 辅助数据源
    druid2:
      url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      initial-size: 1
      max-active: 20
      min-idle: 1
      max-wait: 60000
      time-between-eviction-runs-millis: 10000
      min-evictable-idle-time-millis: 300000

# Druid配置
druid:
  stat-view-servlet:
    enabled: true
    url-pattern: /druid/*
    reset-enable: false
    login-username: druid
    login-password: druid
  filter:
    stat:
      log-slow-sql: true
      slow-sql-millis: 2000
      merge-sql: true
    wall:
      enabled: true
      configs:
        # 允许一次执行多条语句
        multi-statement-allow: true

# MyBatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.demo.model

3. 配置Druid监控

可以通过如下配置,开启Druid的监控:

@Configuration
public class DruidConfig {
    @Bean
    public ServletRegistrationBean statViewServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        // IP白名单设置
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // IP黑名单设置
        servletRegistrationBean.addInitParameter("deny", "192.168.0.1");
        // 控制台管理用户
        servletRegistrationBean.addInitParameter("loginUsername", "druid");
        servletRegistrationBean.addInitParameter("loginPassword", "druid");
        // 是否能够重置数据
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean statFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        // IP白名单设置
        filterRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // IP黑名单设置,如果allow与deny同时存在时,deny优先于allow
        filterRegistrationBean.addInitParameter("deny", "192.168.0.1");
        // 监控路径
        filterRegistrationBean.addUrlPatterns("/*");
        // 排除过滤的请求
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}

三、动态配置多数据源

1. 定义数据源枚举类

public enum DataSourceEnum {
    ds1("主数据源"),
    ds2("辅助数据源");

    private String desc;

    DataSourceEnum(String desc) {
        this.desc = desc;
    }

    public String getDesc() {
        return desc;
    }
}

2. 定义动态数据源

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceKey();
    }
}

3. 定义数据源上下文

public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void set(String dbType) {
        contextHolder.set(dbType);
    }

    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    public static void clear() {
        contextHolder.remove();
    }
}

4. 配置动态数据源

@Configuration
public class DataSourceConfig {
    @Bean(name = "dataSource")
    public DynamicDataSource dataSource() {
        DynamicDataSource dataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnum.ds1.name(), ds1());
        targetDataSources.put(DataSourceEnum.ds2.name(), ds2());
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(ds1());
        return dataSource;
    }

    @Bean(name = "ds1")
    @ConfigurationProperties(prefix = "spring.datasource.druid")
    public DataSource ds1() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "ds2")
    @ConfigurationProperties(prefix = "spring.datasource.druid2")
    public DataSource ds2() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sessionFactory.getObject();
    }
}

5. 定义切换数据源的注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSwitch {
    String value() default DataSourceEnum.ds1;
}

6. 定义切面切换数据源

@Aspect
@Component
public class DataSourceAspect {
    @Pointcut("@annotation(com.example.demo.datasource.DataSourceSwitch)")
    public void dataSourceSwitch() {
    }

    @Before("dataSourceSwitch() && @annotation(dataSourceSwitch)")
    public void before(JoinPoint point, DataSourceSwitch dataSourceSwitch) {
        String dataSourceKey = dataSourceSwitch.value().name();
        if (!DynamicDataSourceContextHolder.containsDataSource(dataSourceKey)) {
            System.err.println("数据源 [{}] 不存在,使用默认数据源 > {}" + dataSourceSwitch.value() + point.getSignature());
        } else {
            System.out.println("Use DataSource :{} > {}" + dataSourceSwitch.value() + point.getSignature());
            DynamicDataSourceContextHolder.setDataSourceKey(dataSourceKey);
        }
    }

    @After("dataSourceSwitch()")
    public void restoreDataSource(JoinPoint point) {
        System.out.println("Revert DataSource : {} > {}"+ DynamicDataSourceContextHolder.getDataSourceKey() + point.getSignature());
        DynamicDataSourceContextHolder.clearDataSourceKey();
    }
}

四、使用示例

1. 业务层接口

public interface UserService {
    List<User> list();

    @DataSourceSwitch(DataSourceEnum.ds2)
    List<User> listByDb2();
}

2. 业务层实现

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> list() {
        return userMapper.selectAll();
    }

    @Override
    public List<User> listByDb2() {
        return userMapper.selectAll();
    }
}

3. 控制层

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/list")
    public List<User> list() {
        return userService.list();
    }

    @GetMapping("/listByDb2")
    public List<User> listByDb2() {
        return userService.listByDb2();
    }
}

五、总结

通过上面的配置,我们可以轻松地实现动态切换数据源的功能,使用起来非常方便。当然,在实际的项目中,可能会有更为复杂的数据源操作,需要我们根据实际情况进行不同的配置和处理,但是,这里的代码可以提供我们一个很好的起点和思路。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用springboot+druid双数据源动态配置操作 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • java 中Map详解及实例代码

    下面是完整的“java 中Map详解及实例代码”攻略。 什么是Map? Map是一种用来存储键-值对数据的数据结构,常用于数据缓存、数据筛选等场景。 Map是一种抽象的数据类型,Java中通过接口Map来定义Map类型。Map接口的实现类有:HashMap、TreeMap、LinkedHashMap 等。 HashMap 什么是HashMap HashMap…

    Java 2023年5月23日
    00
  • Java Apache POI报错“NullPointerException”的原因与解决办法

    “NullPointerException”是Java的Apache POI类库中的一个异常,通常由以下原因之一引起: 空指针错误:如果对象为null,则可能会出现此异常。例如,可能会尝试使用null对象调用方法或尝试访问null对象的属性。 以下是两个实例: 例1 如果对象为null,则可以尝试使用正确的对象以解决此问题。例如,在Java中,可以使用以下代…

    Java 2023年5月5日
    00
  • Mybatis的特点及优点

    让我来详细讲解一下Mybatis的特点及优点。 Mybatis的特点 是一款基于Java的ORM框架,它跟Hibernate等ORM框架不同的是,它对数据库的操作都是通过sql语句进行的,不需要编写复杂的持久化逻辑。因此,Mybatis具有以下几个特点: 1. SQL控制能力强 Mybatis允许开发者自定义SQL语句,并提供了非常灵活的SQL执行方式。开发…

    Java 2023年5月20日
    00
  • Apache Hudi结合Flink的亿级数据入湖实践解析

    下面我来详细讲解一下Apache Hudi结合Flink的亿级数据入湖实践解析的完整攻略。 概述 本文主要介绍如何使用Apache Hudi和Flink实现亿级数据的入湖操作。Hudi是一个可靠的增量数据处理框架,适用于在Apache Spark等大数据处理框架上进行大数据增量计算。而Flink则是一个分布式流处理框架,具有高吞吐量和低延迟的特点。将两者结合…

    Java 2023年5月20日
    00
  • 安装Zookeeper和Kafka集群

    安装Zookeeper和Kafka集群 本文介绍如何安装Zookeeper和Kafka集群。为了方便,介绍的是在一台服务器上的安装,实际应该安装在多台服务器上,但步骤是一样的。 安装Zookeeper集群 下载安装包 从官网上下载安装包: curl https://dlcdn.apache.org/zookeeper/zookeeper-3.7.1/apac…

    Java 2023年4月17日
    00
  • jsp+ajax实现无刷新上传文件的方法

    下面是“jsp+ajax实现无刷新上传文件的方法”的详细攻略: 1. 确定文件上传的目录 首先,我们需要确定文件上传到服务器的目录。可以在项目的WEB-INF目录下创建一个upload文件夹,用于存放上传的文件。 2. 编写HTML代码 接下来,我们需要编写HTML代码,用于用户在页面上选择要上传的文件,并且实现无刷新上传文件的功能。代码如下所示: <…

    Java 2023年6月15日
    00
  • JSP隐含对象response实现文件下载的两种方法

    以下是JSP中使用response实现文件下载的两种方法的详细攻略: 1. 使用response的setHeader()方法: 1.1 步骤: 在JSP页面中,需要一个超链接或者按钮,通过它来触发文件下载,比如: html <a href=”download.jsp?file=test.txt”>下载文件</a> 在download.…

    Java 2023年6月15日
    00
  • 详解DES加密算法的原理与Java实现

    我会详细讲解“详解DES加密算法的原理与Java实现”的完整攻略,并包含两条示例说明。 一、DES加密算法的原理 DES是一种分组加密算法,加密时将明文分成64位一组的大小,每组的最后一位用于存储校验位。DES总共使用16个循环轮次(每轮使用一个48位的密钥子)。第一轮会将明文分成左右两部分,右部分通过跟密钥进行一个函数F运算,F函数使得输入的较小变成较大,…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部