springboot基于方法级别注解事务的多数据源切换问题

摘要:
SpringBoot多数据源配置读取数据源@Component@ConfigurationProperties(prefix=“jdbc.read”)@PropertySource(“classpath:application.properties”)publicclassReadDataSource{privateStringuserName;privateStringpassword;p

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基于方法级别注解事务的多数据源切换问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇CSS基础学习HTTP协议中POST、GET、HEAD、PUT等请求方法以及一些常见错误下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

为什么完整备份不能截断事务日志

导言 完整备份不能截断事务日志,这是所有SQL Server DBA的一个常识, 为此,当数据库处于完整恢复模式时(非特别说明,下文所提到都是完整恢复模式下的数据库),DBA们必须频繁地使用事务日志备份的方式来防止日志文件变得过大。 这几乎成为了DBA们的一个定理,但,作为一个DBA,你证明过这个定理吗?你知道为什么完整备份不能截断事务日志吗? 一个错误的...

查看SQL Server被锁的表以及如何解锁

锁定数据库的一个表的区别   SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除   SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除 SELECT语句中的各项“加锁选项”以及相应的功能说明。 NOLOCK(不加锁) 此选项被选中时,SQL Ser...

SQL Server中的锁类型及用法(转载)

一. 为什么要引入锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 丢失更新 A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统 脏读 A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致 不可重复读 A用户...

MySQL事务隔离级别

 SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。 Read Uncommitted(读取未提交内容)       在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提...

Innodb buffer pool/redo log_buffer 相关

InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。在数据库系统中,由于CPU速度和磁盘速度之前的鸿沟,通常使用缓冲池技术来提高数据库的整体性能。 1. Innodb_buffer_pool 缓冲池(buffer pool)简单来说就是一块内存区域。缓冲池中缓存的数据页类型有:索引页、数据页、undo页、插入缓冲、自适应哈希索引、I...

Oracle锁表查询和解锁方法

数据库操作语句的分类 DML:数据操纵语言,关键字:Insert、delete、update DCL:数据库控制语言 ,关键字:grant、remove DQL:数据库查询语言,关键字:select DDL:数据库模式定义语言,关键字:create oracle表在什么情况下会被锁住 DML锁又可以分为,行锁、表锁、死锁 行锁:当事务执行数据库插入、更...