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中这样定义
- 配置文件
#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
- 数据源标识
- 本地线程共享对象
- 继承AbstractRoutingDataSource
- 指定一下数据源信息
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()); } }
配置切面
在哪个地方识别加到那里
完成

浙公网安备 33010602011771号