springboot基于方法级别注解事务的多数据源切换问题
springBoot多数据源配置
配置读数据源
@Component
@ConfigurationProperties(prefix = "jdbc.read")
@PropertySource("classpath:application.properties")
public class ReadDataSource{
private String userName;
private String password;
private String driver;
private String url; //TODO 此处应有get set方法
}
配置写数据源
@Component
@ConfigurationProperties(prefix = "jdbc.read")
@PropertySource("classpath:application.properties")
public class WriteDataSource{
private String userName;
private String password;
private String driver;
private String url; //TODO 此处应有get set方法
}
//配置数据源适配器 通过此类的set方法可以动态切换数据源,我们只需出入数据源对应key即可
public class DataSourceHolder {
private static final ThreadLocal<String> dataSourceTypes = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "writeDataSource";
}
};
public static String get() {
if(StringUtils.isEmpty(dataSourceTypes.get())){
return "writeDataSource";
}
return dataSourceTypes.get();
}
public static void set(String dataSourceType) {
dataSourceTypes.set(dataSourceType);
}
public static void reset() {
dataSourceTypes.set("writeDataSource");
}
public static void remove() {
dataSourceTypes.remove();
}
}
配置多数据源 此处多数据源的动态切换主要就是通过determineCurrentLookupKey获取对应数据源的key去决定使用哪个数据源
此处需要注意如果处于同一事务中,则数据源不可切换,在事务中,会直接去获取上一次缓存的数据源,没有则调用该方法获取,但只获取一次,所以有可能会导致数据源切换失败.后续我们会通过切面去清除缓存数据源.但仅仅是拿到开启事务第一次获取的数据源.
public class MultipleDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.get();
}
}
@ConfigurationProperties(prefix = "jdbc.read")此处映射以jdbc.read开头的配置属性名和实体类属性名一致
@PropertySource("classpath:application.properties") 指定从那个属性配置文件读取数据源,我的是Maven项目,所以放在resources下
注意:必须要能够被spring管理起来,所以需要配置到spring扫描路径.
接下来我们需要一个配置类:配置多数据源
//basePackages 指定读和写mapper包位置
@Configuration
@MapperScan(basePackages = {"com.xxx.template.dal.mapper.read","com.xxx.template.dal.mapper.write"},sqlSessionTemplateRef = "sqlSessionTemplate")
public Class DataSourceConfig{ @AutoWried
private ReadDataSource readDataSourceProperties;
@AutoWried
private ReadDataSource writeDataSourceProperties;
//配置读数据源属性 @Bean(destroyMethod = "close") public BasicDataSource readDataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(readDataSourceProperties.getDriver()); dataSource.setUrl(readDataSourceProperties.getUrl()); dataSource.setUsername(readDataSourceProperties.getUserName()); dataSource.setPassword(readDataSourceProperties.getPassword()); dataSource.setInitialSize(readDataSourceProperties.getInitialSize()); dataSource.setMaxTotal(readDataSourceProperties.getMaxTotal()); dataSource.setMaxIdle(readDataSourceProperties.getMaxIdle()); dataSource.setRemoveAbandonedOnBorrow(true); dataSource.setRemoveAbandonedTimeout(10); dataSource.setMaxWaitMillis(30000); dataSource.setTestWhileIdle(true); dataSource.setTestOnBorrow(false); dataSource.setTestOnReturn(false); dataSource.setValidationQuery("SELECT 1"); dataSource.setTimeBetweenEvictionRunsMillis(30000); dataSource.setNumTestsPerEvictionRun(30); dataSource.setMinEvictableIdleTimeMillis(600000); return dataSource; } //配置写数据源属性 @Bean(destroyMethod = "close") public BasicDataSource writeDataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(writeDataSourceProperties.getDriver()); dataSource.setUrl(writeDataSourceProperties.getUrl()); dataSource.setUsername(writeDataSourceProperties.getUserName()); dataSource.setPassword(writeDataSourceProperties.getPassword()); dataSource.setInitialSize(writeDataSourceProperties.getInitialSize()); dataSource.setMaxTotal(writeDataSourceProperties.getMaxTotal()); dataSource.setMaxIdle(writeDataSourceProperties.getMaxIdle()); dataSource.setRemoveAbandonedOnBorrow(true); dataSource.setRemoveAbandonedTimeout(10); dataSource.setMaxWaitMillis(30000); dataSource.setTestWhileIdle(true); dataSource.setTestOnBorrow(false); dataSource.setTestOnReturn(false); dataSource.setValidationQuery("SELECT 1"); dataSource.setTimeBetweenEvictionRunsMillis(30000); dataSource.setNumTestsPerEvictionRun(30); dataSource.setMinEvictableIdleTimeMillis(600000); return dataSource; }
//配置动态数据源属性 动态数据源包含读写数据源
@Bean
public MultipleDataSource dataSource() {
MultipleDataSource multipleDataSource = new MultipleDataSource();
Map<Object, Object> map = new HashMap<>();
map.put("readDataSource",readDataSource());
map.put("writeDataSource" ,writeDataSource());
//此处存放多数据源进入map,根据key动态切换
multipleDataSource.setTargetDataSources(map);
return multipleDataSource;
}
//配置sqlSessionFactory
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
//指定mapper.xml的位置
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(DataSourceConfig.MAPPER_LOCATION));
//配置mybatis配置的位置
sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(DataSourceConfig.CONFIG_LOCATION));
return sqlSessionFactoryBean;
}
//配置sqlSessionTemplate
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject());
return sqlSessionTemplate;
}
//配置事务管理
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource());
return dataSourceTransactionManager;
}
}
此刻我们数据源已经配好,接下来可以手动切换数据源,通过DataSourceHolder 的各种方法获取,清除,重置.使用完数据源做好调用清除方法,避免缓存导致无法切换数据源
我们也可以指定一个切面类去动态切换数据源
@Aspect
@Order(-1)
@Component
public class DataSourceSwitch { @Before("此处填写切入点表达式")
public void before(){
切换为读数据源
DataSourceHolder.set("writeDataSource");
}
@Before("此处填写切入点表达式")
public void before1(){
切换为读数据源
DataSourceHolder.set("readDataSource");
} @After("此处填写切入点表达式")
public void after(){
//移除数据源
DataSourceHolder.remove();
}
@After("此处填写切入点表达式")
public void after1(){ //移除数据源
DataSourceHolder.remove();
} }
在多数据源和事务结合起来的情况下,无法一个事务下切换数据源,因此只能一个事务下指定一个数据源,比如我们想读和写,那么最好使用写数据源,只读就只指定读数据源.
最后在我们方法级别加上@Transactional
在启动类上加@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})排除spring的默认数据源配置
springboot基于方法级别注解事务的多数据源切换问题的更多相关文章
- redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现
本次分享如何使用redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现思路 现在的互联网公司大多数都是以Redis作为缓存,使用缓存的优点就不赘述了,写这篇文章的目的就是想帮助同学们如 ...
- springboot项目自定义注解实现的多数据源切换
一.主要依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spri ...
- dubbo服务+Spring事务+AOP动态数据源切换 出错
1:问题描述,以及分析 项目用了spring数据源动态切换,服务用的是dubbo.在运行一段时间后程序异常,更新操作没有切换到主库上. 这个问题在先调用读操作后再调用写操作会出现. 经日志分析原因: ...
- SpringBoot 内部方法调用,事务不起作用的原因及解决办法
在做业务开发时,遇到了一个事务不起作用的问题.大概流程是这样的,方法内部的定时任务调用了一个带事务的方法,失败后事务没有回滚.查阅资料后,问题得到解决,记录下来分享给大家. 场景 我在这里模拟一个场景 ...
- Transaction事务注解和DynamicDataSource动态数据源切换问题解决
问题描述: 写主库开事务的情况下会导致时不时的将更新/插入操作写入到从库上, 导致mysqlException update command denied 问题原因: jetty的工作队列会重用处 ...
- SpringBoot 基于lettuce 连接池 配置redis多数据源操作 生产配置
添加pom<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons- ...
- 后端基于方法的权限控制--Spirng-Security
后端基于方法的权限控制--Spirng-Security 默认情况下, Spring Security 并不启用方法级的安全管控. 启用方法级的管控后, 可以针对不同的方法通过注解设置不同的访问条件: ...
- Spring_AOP基于AspectJ的注解开发&JDBC的模板使用&事务管理(学习笔记3)
一:AOP基于AspectJ的注解开发 1,简单的实例: 1)引入相应的jar包 2)在配置文件里引入相关约束 <beans xmlns="http://www.springfra ...
- SpringBoot 注解事务声明式事务
转载请注明: http://www.cnblogs.com/guozp/articles/7446477.html springboot 对新人来说可能上手比springmvc要快,但是对于各位从sp ...
随机推荐
- 【winform-窗体快捷键】定义功能窗体快捷键,非全局
这里的快捷键并非系统全局快捷键.仅是普通的当窗体在焦点内发生.有很多种方法,这里列举几种项目中使用到的方法. Alt+*(一般控件快捷键) 这个比较简单,只需为该控件的Text属性声明时加上”(&am ...
- Python 面向对象Ⅴ
基础重载方法 下表列出了一些通用的功能,你可以在自己的类重写: 运算符重载 Python同样支持运算符重载,实例如下: 以上代码执行结果如下所示: 类属性与方法 类的私有属性 __private_at ...
- mvc api 关于 post 跟get 请求的一些想法[FromUri] 跟[FromBody] 同一个控制器如何实现共存
wep api 在设置接收请求参数的时候,会自动根据模型进行解析. [FromUrl] 跟[FromBody] 不可以同时使用. 要拆分开: [HttpGet] public object GetP ...
- head first 设计模式笔记2-观察者模式:气象观测站
设计原则:为了交互对象之间的松耦合设计而努力. 1.设计模式的一些理解 1)知道OO基础,并不足以让你设计出良好的OO系统 2)良好的OO设计必须具备可复用.可扩充.可维护三个特性 3)模式可以让我们 ...
- TTTTTTTTTTTTTT CF 645D 点的优先级
题意:给你n个节点,m对优先级关系,a[i] b[i]代表a[i]的优先级比b[i]高,现在问你至少需要前多少对关系就能确定所有节点的优先级: #include <iostream> #i ...
- Springboot 解决跨域请求
Cors处理 跨域请求 细粒度 直接在controller层上 添加@CrossOrigin注解 @PostMapping("/") @CrossOrigin(value = &q ...
- AbpUser 扩展
AbpUser表存放的信息比较少,现扩展一下信息 1.在Core层添加UserExtend 类,继承 AbpUser<User>,写入以上各项属性字段,并添加Discriminator 字 ...
- js+jq 淡入淡出轮播(点击+定时+鼠标进入移出事件)
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title> ...
- java命令--jstack 工具【转载】
一.介绍 jstack是java虚拟机自带的一种堆栈跟踪工具.jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项&qu ...
- 数据预测算法-ARIMA预测
简介 ARIMA: AutoRegressive Integrated Moving Average ARIMA是两个算法的结合:AR和MA.其公式如下: 是白噪声,均值为0, C是常数. ARIMA ...