DataSource相关学习(三)

DruidDataSource 的配置与介绍

在实际工作中,由于 HikariCP 和 Druid 各有千秋,国内的很多开发者都使用 Druid 作为数据源,我们看看都是怎么配置的,每一步都很简单。
第一步:引入依赖。
两种方式,一种是基于Maven的,另外一种是Gradle

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.1</version>
        </dependency>
implementation 'com.alibaba:druid-spring-boot-starter:1.2.1'

第二步:配置数据源。

spring.datasource.druid.url= # 或spring.datasource.url= 
spring.datasource.druid.username= # 或spring.datasource.username=
spring.datasource.druid.password= # 或spring.datasource.password=
spring.datasource.druid.driver-class-name= #或 spring.datasource.driver-class-name=

第三步:配置连接池。

spring.datasource.druid.initial-size=
spring.datasource.druid.max-active=
spring.datasource.druid.min-idle=
spring.datasource.druid.max-wait=
spring.datasource.druid.pool-prepared-statements=
spring.datasource.druid.max-pool-prepared-statement-per-connection-size= 
spring.datasource.druid.max-open-prepared-statements= #和上面的等价
spring.datasource.druid.validation-query=
spring.datasource.druid.validation-query-timeout=
spring.datasource.druid.test-on-borrow=
spring.datasource.druid.test-on-return=
spring.datasource.druid.test-while-idle=
spring.datasource.druid.time-between-eviction-runs-millis=
spring.datasource.druid.min-evictable-idle-time-millis=
spring.datasource.druid.max-evictable-idle-time-millis=
spring.datasource.druid.filters= #配置多个英文逗号分隔
....//more

通过以上三步就可以完成 Druid 数据源的配置了。

生产环境多数据源的处理方法

第一种方式:多个数据源的 @Configuration 的配置方法
可以参考Mybatis配置多数据源(pgsql和mysql)

第二种方式:利用 AbstractRoutingDataSource 配置多数据源
AbstractRoutingDataSource 帮我们实现了动态获得数据源的可能性。下面还是通过一个例子看一下它是怎么使用的。
第一步:定一个数据源的枚举类,用来标示数据源有哪些。

/**
 * 定义一个数据源的枚举类
 */
public enum RoutingDataSourceEnum {
   DB1, //实际工作中枚举的语义可以更加明确一点;
   DB2;
   public static RoutingDataSourceEnum findbyCode(String dbRouting) {
      for (RoutingDataSourceEnum e : values()) {
         if (e.name().equals(dbRouting)) {
            return e;
         }
      }
      return db1;//没找到的情况下,默认返回数据源1
   }
}

第二步:新增 DataSourceRoutingHolder,用来存储当前线程需要采用的数据源。

/**
 * 利用ThreadLocal来存储,当前的线程使用的数据
 */
public class DataSourceRoutingHolder {
   private static ThreadLocal<RoutingDataSourceEnum> threadLocal = new ThreadLocal<>();
   public static void setBranchContext(RoutingDataSourceEnum dataSourceEnum) {
      threadLocal.set(dataSourceEnum);
   }
   public static RoutingDataSourceEnum getBranchContext() {
      return threadLocal.get();
   }
   public static void clearBranchContext() {
      threadLocal.remove();
   }
}

第三步:配置 RoutingDataSourceConfig,用来指定哪些 Entity 和 Repository 采用动态数据源。

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
      //数据源的repository的包路径,这里我们覆盖db1和db2的包路径
      basePackages = {"com.example.jpa.example1"},
      entityManagerFactoryRef = "routingEntityManagerFactory",
      transactionManagerRef = "routingTransactionManager"
)
public class RoutingDataSourceConfig {
   @Autowired
   @Qualifier("db1DataSource")
   private DataSource db1DataSource;
   @Autowired
   @Qualifier("db2DataSource")
   private DataSource db2DataSource;
   /**
    * 创建RoutingDataSource,引用我们之前配置的db1DataSource和db2DataSource
    *
    * @return
    */
   @Bean(name = "routingDataSource")
   public DataSource dataSource() {
      Map<Object, Object> dataSourceMap = Maps.newHashMap();
      dataSourceMap.put(RoutingDataSourceEnum.DB1, db1DataSource);
      dataSourceMap.put(RoutingDataSourceEnum.DB2, db2DataSource);
      RoutingDataSource routingDataSource = new RoutingDataSource();
      //设置RoutingDataSource的默认数据源
      routingDataSource.setDefaultTargetDataSource(db1DataSource);
      //设置RoutingDataSource的数据源列表
      routingDataSource.setTargetDataSources(dataSourceMap);
      return routingDataSource;
   }
   /**
    * 类似db1和db2的配置,唯一不同的是,这里采用routingDataSource
    * @param builder
    * @param routingDataSource entityManager依赖routingDataSource
    * @return
    */
   @Bean(name = "routingEntityManagerFactory")
   public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("routingDataSource") DataSource routingDataSource) {
      return builder.dataSource(routingDataSource).packages("com.example.jpa.example1") //数据routing的实体所在的路径,这里我们覆盖db1和db2的路径
            .persistenceUnit("db-routing")// persistenceUnit的名字采用db-routing
            .build();
   }
   /**
    * 配置数据的事务管理者,命名为routingTransactionManager依赖routtingEntityManagerFactory
    *
    * @param routingEntityManagerFactory
    * @return
    */
   @Bean(name = "routingTransactionManager")
   public PlatformTransactionManager transactionManager(@Qualifier("routingEntityManagerFactory") EntityManagerFactory routingEntityManagerFactory) {
      return new JpaTransactionManager(routingEntityManagerFactory);
   }
}

路由数据源配置与 DataSource1Config 和 DataSource2Config 有相互覆盖关系,这里我们直接覆盖 db1 和 db2 的包路径,以便于我们的动态数据源生效。
第四步:写一个 MVC 拦截器,用来指定请求分别采用什么数据源。
新建一个类 DataSourceInterceptor,用来在请求前后指定数据源:

/**
 * 动态路由的实现逻辑,我们通过请求里面的db-routing,来指定此请求采用什么数据源
 */
@Component
public class DataSourceInterceptor extends HandlerInterceptorAdapter {
   /**
    * 请求处理之前更改线程里面的数据源
    */
   @Override
   public boolean preHandle(HttpServletRequest request,
                      HttpServletResponse response, Object handler) throws Exception {
      String dbRouting = request.getHeader("db-routing");
      DataSourceRoutingHolder.setBranchContext(RoutingDataSourceEnum.findByCode(dbRouting));
      return super.preHandle(request, response, handler);
   }
   /**
    * 请求结束之后清理线程里面的数据源
    */
   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      super.afterCompletion(request, response, handler, ex);
      DataSourceRoutingHolder.clearBranchContext();
   }
}

同时我们需要在实现 WebMvcConfigurer 的配置里面,把我们自定义拦截器 dataSourceInterceptor 加载进去,代码如下:

/**
 * 实现WebMvcConfigurer
 */
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
   @Autowired
   private DataSourceInterceptor dataSourceInterceptor;
   //添加自定义拦截器
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(dataSourceInterceptor).addPathPatterns("/**");
      WebMvcConfigurer.super.addInterceptors(registry);
   }
...//其他不变的代码省略}

此处我们采用的是 MVC 的拦截器机制动态改变的数据配置,你也可以使用自己的 AOP 任意的拦截器,如事务拦截器、Service 的拦截器等,都可以实现。需要注意的是,要在开启事务之前配置完毕。
第五步:启动测试。
我们在 Http 请求头里面加上 db-routing:DB2,那么本次请求就会采用数据源 2 进行处理,请求代码如下:

POST /user/info HTTP/1.1
Host: 127.0.0.1:8089
Content-Type: application/json
db-routing: DB2
Cache-Control: no-cache
Postman-Token: 56d8dc02-7f3e-7b95-7ff1-572a4bb7d102
{"ages":10}

由于实际问题可能更为复杂,需要考虑多线程、线程安全等问题,因此对于我工作中的项目使用较少。只是简单学习下~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值