SSM框架使用多数据源(druid连接池)
创始人
2024-02-08 00:34:10
0

 最近有个数据归集的需求用到了多数据源,在业务库保存后同时向归集库插入或数据。之前好像还没做过这块的东西,简单记录下防止下次又忘记了~

踩过的几个坑都是某些知识点不熟悉导致的,而且都是框架配置相关的..

先上代码,再扯淡

两个库都是mysql,不同数据库应该就是配置不一样,使用的druid数据库连接池

一、修改properties配置文件中的数据库信息

#jdbc configureconnection.url=${db.url}
connection.username=${db.username}
connection.password=${db.password}
connection.driver=${connection.driver_class}#删掉此配置 退回单数据源
connection.url2=${tradding.db.url2}
connection.username2=${tradding.db.username2}
connection.password2=${tradding.db.password2}
connection.driver2=${connection.driver_class2}

二、创建@DataSourceAnnotation 注解使用于aop进行数据源的切换

package cn.xxx.datasource;import java.lang.annotation.*;/*** @author zj* @creatTime 2022-11-22* @description*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceAnnotation {String value();String primary = "primary";String secend= "secend";}

三、创建动态数据源DynamicDataSource继承AbstractRoutingDataSource

package cn.xxx.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** @author zj* @creatTime 2022-11-18* @description 配置多个数据源*/
public class DynamicDataSource extends AbstractRoutingDataSource {/*** 数据源标识,保存在线程变量中,避免多线程操作数据源时互相干扰*/private static final ThreadLocal key = new ThreadLocal();@Overrideprotected Object determineCurrentLookupKey() {return key.get();}/*** 设置数据源** @param dataSource 数据源名称*/public static void setDataSource(String dataSource) {key.set(dataSource);}/*** 获取数据源** @return*/public static String getDatasource() {return key.get();}/*** 清除数据源*/public static void clearDataSource() {key.remove();}}

四、创建一个aop切面作用于目标方法上

package cn.xxx.datasource;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;/*** @author zj* @creatTime 2022-11-22* @description*/
@Component
public class DynamicDataSourceAspect implements MethodBeforeAdvice, AfterReturningAdvice {Logger log = LoggerFactory.getLogger(DynamicDataSourceAspect.class);@Overridepublic void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {//这里做一个判断,有使用DataSourceAnnotation注解时才关闭数据源,有一个主要的数据源,就没有必要每次都去关闭if (method.isAnnotationPresent(DataSourceAnnotation.class)) {DynamicDataSource.clearDataSource();log.debug("数据源已关闭");}}/*** 拦截目标方法,获取由@DataSourceAnnotation指定的数据源标识,设置到线程存储中以便切换数据源*/@Overridepublic void before(Method method, Object[] objects, Object o) throws Throwable {if (method.isAnnotationPresent(DataSourceAnnotation.class)) {DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class);DynamicDataSource.setDataSource(dataSourceAnnotation.value());log.debug("数据源切换为:" + DynamicDataSource.getDatasource());}}
}

五、spring-config.xml配置多数据源

  

六、配置事务和AOP

 

这里需要注意的是order值越小,优先级越高,所以切换数据源order的值要比事务切面的值小,否则会出现数据源切换失败!

七、切换数据源,实际使用

在需要切换为非默认数据的方法上加@DataSourceAnnotation(DataSourceAnnotation.secend)就可以完成数据源的切换了。

controller层:

/*** @author zj* @creatTime 2022-11-18* @description*/
@Controller
public class TestController {@Autowiredprivate TPProjectMapper projectMapper;@Autowiredprivate TestSourceService testSourceService;@RequestMapping("/ocx/saveOrUpdateTest")@ResponseBodypublic Result saveOrUpdateTest(){Result result = new Result();//先测试插入 监督库TPProject project = new TPProject();project.setProjectId(111221212L);project.setProjectName("test测试多数据源——监督库");ContextFacade.initEntity(project);projectMapper.insert(project);//再测试插入 交易库testSourceService.saveOrUpdateTest();//再测试修改 监督库TPProject project1 = projectMapper.selectByPrimaryKey(111221212L);project1.setProjectId(111221212L);project1.setProjectName("test测试多数据源——监督库__二次修改结果");projectMapper.updateByPrimaryKey(project1);return result;}}

service层:

package cn.xxx.test.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @author zj* @creatTime 2022-11-18* @description*/
@Service
public class TestSourceService {@Autowiredprivate TPProjectMapper tpProjectMapper;@DataSourceAnnotation(DataSourceAnnotation.secend)public Result saveOrUpdateTest(){Result result = new Result();TPProject project = new TPProject();project.setProjectId(111221213L);project.setProjectName("test测试多数据源——交易库");ContextFacade.initEntity(project);tpProjectMapper.insert(project);return result;}
}

八、结果

主库:

副库:

 这里为了方便用了同一个表,只是不同数据库,所以mapper和sql也一样的,实际根据需求来就行。

九、Other

1、SpringAOP切面类不运行的问题 ,注意配置写在spring-config.xml而不是spring-mvc.xml中。

2、aop:pointcut的配置说明

其中expression="execution(* com.aop.service..*(..))"的配置规则如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
execution(方法的操作权限    返回值类型模式    方法所在的包        方法名 (参数名)  异常)

参数名(参数模式)稍微复杂一些:
()                 匹配不带参数的方法,
(..)               匹配任何数量(零个或多个)参数。
(*)                模式匹配采用任何类型的一个参数的方法
(*,String)     匹配一个带有两个参数的方法。第一个可以是任何类型,而第二个必须是String


返回值,方法名,参数名,必须有,其他可选


执行任何公共方法:
execution(public * *(..))


执行名称以以下开头的任何方法set:
execution(* set*(..))


执行AccountService接口定义的任何方法:
execution(* com.xyz.service.AccountService.*(..))


执行service包中定义的任何方法:
execution(* com.xyz.service.*.*(..))


执行服务包或其子包中定义的任何方法:
execution(* com.xyz.service..*.*(..))

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...