手把手教你用springboot打造标准系统(二)--多数据源

明确几个问题:

什么是标准系统?
标准系统就是系统架构师初始系统,包括业务系统的所有基础集成技术,可以在这个系统上开发出任何应用系统。

标准系统使用的技术和最终的目标
a、使用springboot2、mybites、redis、session共享、Shiro、quartz、JWT等技术,

b、集成MyBites,使用druid做数据链接池,支持多数据源;

c、集成session共享,支持系统集群;

d、集成Shiro权限管理系统;

e、采用RESTFul风格;

f、采用JWT完成用户认证授权;

g、使用quartz完成任务管理;
 

多数据源一般采用切面的方式来实现,多数据源的好处有很多,比如读写分离,数据库分库等性能优化。

下面我们实现2个数据源,跟多的数据源可以自己进行扩展。如果数据库多到4个,建议做微服。

创建数据源名称接口类:

package com.james.framework.datasources;


public interface DataSourceNames {
    String FIRST = "first";
    String SECOND = "second";

}

然后定义一个annotation,用于注解用;

package com.james.framework.datasources.annotation;

import java.lang.annotation.*;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String name() default "";
}

然后我们继承spring里的AbstractRoutingDataSource类

package com.james.framework.datasources;

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

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


public class DynamicDataSource extends AbstractRoutingDataSource {
    //线程安全
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    //动态数据源
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(new HashMap<>(targetDataSources));
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

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

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

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

}

然后我们添加一个config,让springboot启动时能够使用动态数据源;

package com.james.framework.datasources;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;


@Configuration
public class DynamicDataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.druid.first")
    public DataSource firstDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource secondDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    public DynamicDataSource myDataSource(DataSource firstDataSource, DataSource secondDataSource) {
        Map<String, DataSource> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
        targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
        //默认第一个链接池
        return new DynamicDataSource(firstDataSource, targetDataSources);
    }
}

注意在第一个数据源上加的@Primary  由于数据源1和2 都是 DataSource,需要标明优先实例化第一个。

修改配置文件,配置多数据源,

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      first:  #数据源1
          url: jdbc:mysql://127.0.0.1:3306/adu_framework?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
          username: root
          password: root
      second:  #数据源2
          url: jdbc:mysql://localhost:3306/adu_framework?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
          username: root
          password: root
      initial-size: 10
      max-active: 100
      min-idle: 10
      max-wait: 60000
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      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
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        #login-username: admin
        #login-password: admin
      filter:
        stat:
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true

大家可以对比上一步单数据源的配置,看看有什么不同。

我们这里是开发模式,所有2个数据源指向类同一个数据库。

那么如何使用这2个数据源呢?

其实很简单,就是在需要使用第二个数据源的情况下申明一下,比如下面的service:

package com.james.framework.modules.sys.service.Impl;

import com.james.framework.datasources.DataSourceNames;
import com.james.framework.datasources.annotation.DataSource;
import com.james.framework.modules.sys.dao.SysUserDao;
import com.james.framework.modules.sys.entity.SysUserEntity;
import com.james.framework.modules.sys.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SysUserServicesImpl implements SysUserService {
    @Autowired
    private SysUserDao sysUserDao;
    @Override
    public List<SysUserEntity> list() {
        return sysUserDao.queryList(null);
    }
    @DataSource(name = DataSourceNames.SECOND)
    public Integer delUser(Integer userId){
        return this.sysUserDao.delete(userId);
    }
}

list方法使用了默认的第一个数据源;

delUser方法通过注解@DataSource申明使用第二个数据源;下面运行测试一下是否可用。

现在数据库里有3个用户,我们要删除第3个用户,还是使用上一步开发的TestController。具体代码查看上一篇博客。

由于浏览器模拟不了DELETE请求,所以我们使用postman:

点击“send”按钮后,body看到是返回了“1”

查看数据库内数据确定被删除了。

 

发布了4 篇原创文章 · 获赞 0 · 访问量 506
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览