资讯详情

Spring Boot 基于Druid连接池的多数据源 + 自定义事务管理器

前言

本文详细介绍了基于阿里巴巴的文章Druid连接池的多数据源和Spring 实现自定义事务管理器的方法。 技术栈: JDK:8 Spring Boot:2.5.5 Mybatis:2.2.0 Druid:1.1.10 (建议使用1.2.8,我受限于Nexus只能用1.1.10)

文章目录

    • 前言
    • Yml配置
    • Druid连接池配置类
    • 配置一般方法
    • 第一个数据源(datasource)配置类
    • 第二个数据源(datasource2)配置类
    • 自定义事务管理器通用方法
    • 自定义第一个数据源的事务管理器
    • 自定义第二个数据源的事务管理器
    • 结语

Yml配置

spring:   datasource:     driver-class-name: com.mysql.cj.jdbc.Driver     type: com.alibaba.druid.pool.DruidDataSource     url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&tinyInt1isBit=false     username: root     password: root   datasource2:     driver-class-name: com.mysql.cj.jdbc.Driver     type: com.alibaba.druid.pool.DruidDataSource     url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&tinyInt1isBit=false     username: root     password: root 

这两个数据源可以是异构数据库(未实测),注意调整相关配置

Druid连接池配置类

package com.xxx.config;

import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

/** * Description: * 阿里巴巴数据库库连接池配置类 * <p> * date: 2021/11/26 15:20 * * @author Arvin Lee * @version 1.0 */
@Configuration
@Slf4j
public class DruidConfig { 
        
    @Value("${spring.datasource.url:#{null}}")
    private String dbUrl;
    @Value("${spring.datasource.username: #{null}}")
    private String username;
    @Value("${spring.datasource.password:#{null}}")
    private String password;
    @Value("${spring.datasource.driverClassName:#{null}}")
    private String driverClassName;

    @Value("${spring.datasource2.url:#{null}}")
    private String dbUrl2;
    @Value("${spring.datasource2.username: #{null}}")
    private String username2;
    @Value("${spring.datasource2.password:#{null}}")
    private String password2;
    @Value("${spring.datasource2.driverClassName:#{null}}")
    private String driverClassName2;

    @Bean
    @Primary
    public DataSource dataSource() { 
        
        return CommonFunctionInConfig.getDataSource(dbUrl,
                username,
                password,
                driverClassName,
                statFilter(), wallFilter());
    }

    @Bean
    public DataSource dataSource2() { 
        
        return CommonFunctionInConfig.getDataSource(dbUrl2, username2, password2, driverClassName2,
                statFilter(), wallFilter());
    }

    @Bean
    public ServletRegistrationBean druidServlet() { 
        
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        //控制台管理用户,加入下面2行 进入druid后台就需要登录
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "admin");
        return servletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() { 
        
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        filterRegistrationBean.addInitParameter("profileEnable", "true");
        return filterRegistrationBean;
    }

    @Bean
    public StatFilter statFilter() { 
        
        StatFilter statFilter = new StatFilter();
        //slowSqlMillis用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢。
        statFilter.setLogSlowSql(true);
        //SQL合并配置
        statFilter.setMergeSql(true);
        //slowSqlMillis的缺省值为3000,也就是3秒。
        statFilter.setSlowSqlMillis(1000);
        return statFilter;
    }

    @Bean
    public WallFilter wallFilter() { 
        
        WallFilter wallFilter = new WallFilter();
        //允许执行多条SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);
        wallFilter.setConfig(config);
        return wallFilter;
    }
}

配置通用方法类

package com.xxx.config;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallFilter;

import java.util.ArrayList;
import java.util.List;

/** * Description: * 配置包下的通用方法 * <p> * date: 2021/11/29 15:06 * * @author Arvin Lee * @version 1.0 */
public class CommonFunctionInConfig { 
        

    /** * getDataSource * 获取数据源 * * @param dbUrl 数据库连接地址 * @param username 用户名 * @param password 密码 * @param driverClassName 驱动名 * @param statFilter - * @param wallFilter - * @return com.alibaba.druid.pool.DruidDataSource * @author Arvin Lee * @date 2021/12/7 9:28 **/
    protected static DruidDataSource getDataSource(String dbUrl,
                                                   String username,
                                                   String password,
                                                   String driverClassName,
                                                   StatFilter statFilter,
                                                   WallFilter wallFilter) { 
        
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        List<Filter> filters = new ArrayList<>();
        filters.add(statFilter);
        filters.add(wallFilter);
        datasource.setProxyFilters(filters);
        return datasource;
    }
}

连接池其他配置参数可以在这里统一调整

第一个数据源(datasource)配置类

package com.xxx.config;

import com.xxx.BaseMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import tk.mybatis.spring.annotation.MapperScan;

import javax.annotation.Resource;
import javax.sql.DataSource;

/** * Description: * mybatis数据源配置 * <p> * date: 2021/11/26 15:39 * * @author Arvin Lee * @version 1.0 */
@Configuration
@MapperScan(basePackages = "com.xxx.dao.datasource1", markerInterface = BaseMapper.class, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceConfig { 
        
    @Resource
    @Qualifier("dataSource")
    private DataSource ds;

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception { 
        
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(ds);
        //指定mapper xml目录
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/datasource1/*.xml"));
        return factoryBean.getObject();

    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate() throws Exception { 
        
        // 使用上面配置的Factory
        return new SqlSessionTemplate(sqlSessionFactory());
    }

    /** * masterTransactionManager * 关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager * 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。 * 在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。 * * @return org.springframework.jdbc.datasource.DataSourceTransactionManager * @author Arvin Lee * @date 2021/11/26 15:42 **/
    @Bean(name = "transactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() { 
        
        //MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源
        // 与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。
        return new DataSourceTransactionManager(ds);

    }
}
  1. 此处需要注意一下,代码经过脱敏,所以 注解的 路径com.xxx.dao.datasource1 大意如下
  2. 对应的是我的Mybatis的BaseMapper,如果没有可以不写。
  3. 参考的sql会话工厂,这个与返回的方法的方法名对应
  4. @ 这是给DataSource起别名,因为是多数据源,命名没有特殊要求。
  5. 这个是mapper.xml映射路径,路径格式参考dao 示例:

第二个数据源(datasource2)配置类

package com.xxx.config;

import com.xxx.common.BaseMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import tk.mybatis.spring.annotation.MapperScan;

import javax.annotation.Resource;
import javax.sql.DataSource;

/** * Description: * mybatis数据源2配置 * <p> * date: 2021/11/26 16:02 * * @author Arvin Lee * @version 1.0 */
@Configuration
@MapperScan(basePackages = "com.xxx.dao.datasource2", markerInterface = BaseMapper.class, sqlSessionFactoryRef = "sqlSessionFactory2")
public class DataSource2Config { 
        
    @Resource
    @Qualifier("dataSource2")
    private DataSource ds;

    @Bean
    public SqlSessionFactory sqlSessionFactory2() throws Exception { 
        
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(ds);
        //指定mapper xml目录
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/datasource2/*.xml"));
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate2() throws Exception { 
        
        // 使用上面配置的Factory
        return new SqlSessionTemplate(sqlSessionFactory2());
    }

    /** * masterTransactionManager * 关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager * 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。 * 在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。 * * @return org.springframework.jdbc.datasource.DataSourceTransactionManager * @author Arvin Lee * @date 2021/11/26 15:42 **/
    @Bean(name = "transactionManager2")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() { 
        
        //MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源
        // 与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。
        return new DataSourceTransactionManager(ds);

    }
}
  1. 格式与第一个一致,只不过dao、mapper.xml、datasource、SQLSessionFactory不同,需要加个2
  2. 如果3个数据源,以此类推,再加个DataSourceConfig 同时也要修改DruidConfig

自定义事务管理器通用方法类

package com.xxx.interceptor;

import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/** * Description: * <p> * date: 2021/11/29 10:11 * * @author Arvin Lee * @version 1.0 */
public class OtherFunction { 
        
    private static final int TX_METHOD_TIMEOUT = 50000;

    protected static TransactionInterceptor getInterceptor(TransactionManager transactionManager) { 
        
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        /*只读事务,不做更新操作*/
        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
        /*当前存在事务就使用当前事务,当前不存在事务就创建一个新的事务*/
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
        requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        requiredTx.setTimeout(TX_METHOD_TIMEOUT);
        Map<String, TransactionAttribute> txMap = new HashMap<>(8);
        txMap.put("add*", requiredTx);
        txMap.put("save*", requiredTx);
        txMap.put("insert*", requiredTx);
        txMap.put("update*", requiredTx);
        txMap.put("delete*", requiredTx);
        txMap.put("get*", readOnlyTx);
        txMap.put("select*", readOnlyTx
        标签: 连接器tk10

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台