ssm多数据源的切换+springboot多数据源

切换数据源总结

第一种方法:ssm框架整合。

切面的配置都是用注解的方式实现的

<context:component-scanbase-package="com.spring.example.aspectAspectJArgs"/>

<aop:aspectj-autoproxy/>

 

1.配置数据源信息。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

<property name="driverClassName" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://192.168.0.223:3306/hbsxt?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true"/>

<property name="username" value="root"/>

<property name="password" value="admin123!@#QWE"/>

<!-- 配置初始化大小、最小、最大 -->

<property name="initialSize" value="3"/>

<property name="minIdle" value="3"/>

<property name="maxActive" value="20"/>

<!-- 配置获取连接等待超时的时间 -->

<property name="maxWait" value="60000"/>

<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->

<property name="timeBetweenEvictionRunsMillis" value="60000"/>

<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->

<property name="minEvictableIdleTimeMillis" value="300000"/>

<property name="validationQuery" value="SELECT 'x'"/>

<property name="testWhileIdle" value="true"/>

<property name="testOnBorrow" value="false"/>

<property name="testOnReturn" value="false"/>

<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->

<property name="poolPreparedStatements" value="true"/>

<property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>

<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->

<property name="filters" value="stat"/>

</bean>

<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

<property name="driverClassName" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://192.168.0.213:3306/hbsxt?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true"/>

<property name="username" value="root"/>

<property name="password" value="admin123!@#QWE"/>

<!-- 配置初始化大小、最小、最大 -->

<property name="initialSize" value="3"/>

<property name="minIdle" value="3"/>

<property name="maxActive" value="20"/>

<!-- 配置获取连接等待超时的时间 -->

<property name="maxWait" value="60000"/>

<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->

<property name="timeBetweenEvictionRunsMillis" value="60000"/>

<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->

<property name="minEvictableIdleTimeMillis" value="300000"/>

<property name="validationQuery" value="SELECT 'x'"/>

<property name="testWhileIdle" value="true"/>

<property name="testOnBorrow" value="false"/>

<property name="testOnReturn" value="false"/>

<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->

<property name="poolPreparedStatements" value="true"/>

<property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>

<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->

<property name="filters" value="stat"/>

</bean>

<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

<property name="driverClassName" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://192.168.0.167:3306/hbsxt?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true"/>

<property name="username" value="root"/>

<property name="password" value="admin123!@#QWE"/>

<!-- 配置初始化大小、最小、最大 -->

<property name="initialSize" value="3"/>

<property name="minIdle" value="3"/>

<property name="maxActive" value="20"/>

<!-- 配置获取连接等待超时的时间 -->

<property name="maxWait" value="60000"/>

<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->

<property name="timeBetweenEvictionRunsMillis" value="60000"/>

<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->

<property name="minEvictableIdleTimeMillis" value="300000"/>

<property name="validationQuery" value="SELECT 'x'"/>

<property name="testWhileIdle" value="true"/>

<property name="testOnBorrow" value="false"/>

<property name="testOnReturn" value="false"/>

<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->

<property name="poolPreparedStatements" value="true"/>

<property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>

<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->

<property name="filters" value="stat"/>

</bean>

<!--读取当前的返回值key值,选择数据源dataSourceOption-->

    <bean id="dataSourceOption" class="com.cn.utils.DynamicDataSourceUtils">

        <!--默认链接dataSource-->

        <property name="defaultTargetDataSource" ref="dataSource"/>

        <property name="targetDataSources">

            <map>

                <entry key="dataSource" value-ref="dataSource"/>

                <entry key="dataSource1" value-ref="dataSource1"/>

                <entry key="dataSource2" value-ref="dataSource2"/>

            </map>

        </property>

</bean> 

 

2.定义一个枚举类,定义一个标识,用来识别数据源。

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface ChooseDataSource {

    String dataSourceName();

}

 

3.定义数据源的信息来进行判断

import java.util.Arrays;

import java.util.List;

public class DataSourceCheckUtils {

    static List<String> souList = Arrays.asList("dataSource", "dataSource1", "dataSource2");

    public static boolean check(String dataSourceName) {

        boolean flag = false;

        if (souList.contains(dataSourceName)) {

            flag = true;

        }

        return flag;

    }

}

 

4.继承AbstractRoutingDataSource这个类来重写determineCurrentLookupKey(切换重要部分)

import org.aopalliance.aop.Advice;

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

public class DynamicDataSourceUtils extends AbstractRoutingDataSource implements Advice {

    @Override

    protected Object determineCurrentLookupKey() {

        String dataSourceName = DynamicDataSourceHolder.getDataSourceName();

        if (dataSourceName == null) {

            dataSourceName = "dataSource";

        }

        System.out.println("数据源:" + dataSourceName);

        return dataSourceName;

    }

}

 

5.定义一个threadLocal来进行管理数据源,防止起冲突

public class DynamicDataSourceHolder {

 
private static final ThreadLocal<String> holder = new ThreadLocal<String>();

  public static void putDataSourceName(String dataName) {

    holder.set(dataName);

  }

  public static String getDataSourceName() {

    return holder.get();

  }

  public static class DataSourceName {

    public final static String dataSource = "dataSource";

  }

}

切面类

@Aspect

@Component

@Order(-1)

public class ChooseDataSourceAspect {

  @Pointcut("execution(* swtech.service.hbsxt.control.*.*(..)) ")

  private void controllerAspect() {
  
  }

  @Before("controllerAspect()")//根据类上面的标识进行切源

  public void switchDataSource(JoinPoint pjp) throws Throwable{

    HttpServletRequest request = RpcContext.getContext().getRequest(HttpServletRequest.class);

    String[] strlist = null;

    String ip, port;

    // 获取路径

    StringBuffer str = request.getRequestURL();

    Pattern pattern = Pattern.compile("https?://([^/]*).*");

    Matcher matcher = pattern.matcher(str);

    if (matcher.find()) {

      String w = matcher.group(1);

      strlist = w.split(":");

    }

    ip = strlist[0];

    port = strlist[1];

    Signature signature = pjp.getSignature();    

    MethodSignature methodSignature = (MethodSignature)signature;  

    Method targetMethod = methodSignature.getMethod();

    Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(signature.getName(), targetMethod.getParameterTypes());

    //首先判断方法级别

    ChooseDataSource cds=realMethod.getAnnotation(ChooseDataSource.class);

       if(cds==null){ //判断类级别

          cds= AnnotationUtils.findAnnotation(realMethod.getDeclaringClass(), ChooseDataSource.class);

       }

       if (cds != null) {

      if(ip.equals("127.0.0.1")) {

        DynamicDataSourceHolder.putDataSourceName("dataSource1");

      }

    }
     }
}

 

注:

/***

*

*aop切源2中方式(方法和类)

*

*/

 @Pointcut("execution(* swtech.service.hbsxt.control.*.*(..)) ")

    private void controllerAspect() {

    }


    @Before("controllerAspect()")//前置增强(根据类名进行)

  public void switchDataSource(JoinPoint pjp) throws Throwable{
    Signature signature = pjp.getSignature();    
    MethodSignature methodSignature = (MethodSignature)signature; 
    Method targetMethod = methodSignature.getMethod();
    Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(signature.getName(), targetMethod.getParameterTypes());
      //首先判断方法级别
    ChooseDataSource cds=realMethod.getAnnotation(ChooseDataSource.class);

      if(cds==null){ //判断类级别
        cds= AnnotationUtils.findAnnotation(realMethod.getDeclaringClass(), ChooseDataSource.class);
      }

      if(cds==null){  //默认库

       DynamicDataSourceHolder.putDataSourceName("dataSource");

       return;
      }

      String dataSourceName=cds.dataSourceName(); //获取注解的值

      if(dataSourceName!=null&&!dataSourceName.equals(""))  //通过数据源路由类切换数据源

            DynamicDataSourceHolder.putDataSourceName(dataSourceName);
      }

//=================================================================================

//环绕增强(根据方法标识))

@Around("controllerAspect()")

public Object permission(ProceedingJoinPoint joinPoint) throws Throwable {

HttpServletRequest request = RpcContext.getContext().getRequest(HttpServletRequest.class);

String[] strlist = null;

String ip, port;

// 获取路径

StringBuffer str = request.getRequestURL();

Pattern pattern = Pattern.compile("https?://([^/]*).*");

Matcher matcher = pattern.matcher(str);

if (matcher.find()) {

  String w = matcher.group(1);

  strlist = w.split(":");

}

ip = strlist[0];

port = strlist[1];


Object target = joinPoint.getTarget();

Object[] args = joinPoint.getArgs();

Method method = getMethod(joinPoint, args);

ChooseDataSource chooseDataSource = method.getAnnotation(ChooseDataSource.class);

if (chooseDataSource != null) {

if(ip.equals("127.0.0.1")) {

  DynamicDataSourceHolder.putDataSourceName("dataSource1");

}else {

  DynamicDataSourceHolder.putDataSourceName("dataSource");

}

}

/*

Object target = joinPoint.getTarget();

Object[] args = joinPoint.getArgs();

Method method = getMethod(joinPoint, args);

//获取注释class对象

ChooseDataSource chooseDataSource = method.getAnnotation(ChooseDataSource.class);

//判断dataSourceName是否为空,如果为空用默认数据源

if (chooseDataSource != null) {

String dataSourceName = chooseDataSource.dataSourceName();

if (DataSourceCheckUtils.check(dataSourceName)) {

DynamicDataSourceHolder.putDataSourceName(dataSourceName);

} else {

DynamicDataSourceHolder.putDataSourceName("dataSource");

}

}*/

return joinPoint.proceed();

}

 

private Method getMethod(ProceedingJoinPoint joinPoint, Object[] args) throws NoSuchMethodException {

//获得方法名称

String methodName = joinPoint.getSignature().getName();

//获取目标对象对应的类名

Class clazz = joinPoint.getTarget().getClass();

 //获取到这个类上面的方法全名

Method[] methods = clazz.getMethods();

for (Method method : methods) {

if (methodName.equals(method.getName())) {

return method;

}

}

return null;

}

完成。

 

第一种方法:ssm框架整合。

解析不同的地方就是配置文件其他地方大致一样:

配置数据源的信息不同,配置文件写在application.properties中这样定义

  1. 配置文件

#test1数据源配置

hikari.test1.jdbc-url=jdbc:mysql://127.0.0.1:3306/${test}?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

hikari.test1.username=root

hikari.test1.password=admin123!@#QWE

hikari.test1.maximum-pool-size=10

 

#test2数据源配置

hikari.test2.jdbc-url=jdbc:mysql://127.0.0.1:3306/${test2}?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

hikari.test2.username=root

hikari.test2.password=admin123!@#QWE

hikari.test2.maximum-pool-size=10
test=test

test2=test2

#mybatis配置

#mybatis.type-aliases-package=com.example.demo.cut.pojo

#mybatis.mapper-locations=classpath:/mapper/*.xml

#前缀

spring.mvc.view.prefix=/WEB-INF/jsp/

#后缀

spring.mvc.view.suffix=.jsp

spring.http.encoding.force=true

spring.http.encoding.charset=UTF-8

spring.http.encoding.enabled=true

server.tomcat.uri-encoding=UTF-8
  1. 数据源标识
  2. 本地线程共享对象
  3. 继承AbstractRoutingDataSource
  4. 指定一下数据源信息
import com.zaxxer.hikari.HikariDataSource;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

 

/**

 * 实际数据源配置

 */

@Component

@Data

@ConfigurationProperties(prefix = "hikari")

public class DBProperties {

 

private HikariDataSource test1;

private HikariDataSource test2;

}

数据源配置类,

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import org.springframework.scheduling.annotation.EnableScheduling;

import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

import java.util.HashMap;

import java.util.Map;

/**

 * 数据源配置配置类

 * 通过@EnableScheduling注解开启对计划任务的支持

 */

@Configuration

@EnableScheduling

public class DataSourceConfig {

@Autowired

private DBProperties properties;

@Bean(name = "dataSource")

public DataSource dataSource() {

//按照目标数据源名称和目标数据源对象的映射存放在Map中

Map<Object, Object> targetDataSources = new HashMap<>();

//把2个数据源存放到Map中

targetDataSources.put("test1", properties.getTest1());

targetDataSources.put("test2", properties.getTest2());

//采用是想AbstractRoutingDataSource的对象包装多数据源

 

DynamicDataSource dataSource = new DynamicDataSource();

dataSource.setTargetDataSources(targetDataSources);

//设置默认的数据源,当拿不到数据源时,使用此配置

dataSource.setDefaultTargetDataSource(properties.getTest1());

return dataSource;

}

@Bean

public PlatformTransactionManager txManager() {

return new DataSourceTransactionManager(dataSource());

}

}

配置切面

在哪个地方识别加到那里

完成

 

posted @ 2019-09-05 20:20  sea-gull  阅读(348)  评论(0)    收藏  举报