| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- package com.poyee.datasource;
- import com.alibaba.druid.pool.DruidDataSource;
- import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
- import com.baomidou.mybatisplus.annotation.DbType;
- import com.baomidou.mybatisplus.core.config.GlobalConfig;
- import com.baomidou.mybatisplus.core.toolkit.StringUtils;
- import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
- import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
- import com.github.yulichang.injector.MPJSqlInjector;
- import com.github.yulichang.interceptor.MPJInterceptor;
- import com.poyee.datasource.common.DbInitUtil;
- import com.poyee.datasource.modal.DbConfigModal;
- import com.poyee.util.ParseUtil;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.ibatis.io.VFS;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.jetbrains.annotations.Nullable;
- import org.mybatis.spring.annotation.MapperScan;
- import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.boot.jdbc.DataSourceBuilder;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Primary;
- import org.springframework.core.env.Environment;
- import org.springframework.core.io.DefaultResourceLoader;
- import org.springframework.core.io.Resource;
- import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
- import org.springframework.core.io.support.ResourcePatternResolver;
- import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
- import org.springframework.core.type.classreading.MetadataReader;
- import org.springframework.core.type.classreading.MetadataReaderFactory;
- import org.springframework.jdbc.datasource.DataSourceTransactionManager;
- import org.springframework.transaction.PlatformTransactionManager;
- import org.springframework.util.ClassUtils;
- import org.springframework.util.CollectionUtils;
- import javax.sql.DataSource;
- import java.io.IOException;
- import java.sql.SQLException;
- import java.util.*;
- /**
- * 多数据源配置
- */
- @Slf4j
- @Configuration
- @MapperScan(basePackages = {"com.poyee.mapper","com.poyee.**.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
- public class DataSourceConfig {
- @Value("${spring.datasource.dynamic.datasource.master.url}")
- private String url;
- // 连接池连接信息
- @Value("${spring.datasource.druid.initialSize}")
- private int initialSize;
- @Value("${spring.datasource.druid.minIdle}")
- private int minIdle;
- @Value("${spring.datasource.druid.maxActive}")
- private int maxActive;
- @Value("${spring.datasource.druid.maxWait}")
- private int maxWait;
- @Value("${spring.profiles.active:dev}")
- private String version;
- static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
- @Autowired
- private Environment env;
- /**
- * @param typeAliasesPackage
- * @return
- */
- private static String setTypeAliasesPackage(String typeAliasesPackage) {
- ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
- MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
- List<String> allResult = new ArrayList<>();
- try {
- for (String aliasesPackage : typeAliasesPackage.split(",")) {
- List<String> result = new ArrayList<>();
- aliasesPackage =
- ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
- Resource[] resources = resolver.getResources(aliasesPackage);
- if (resources != null && resources.length > 0) {
- MetadataReader metadataReader = null;
- for (Resource resource : resources) {
- if (resource.isReadable()) {
- metadataReader = metadataReaderFactory.getMetadataReader(resource);
- try {
- result.add(Class.forName(metadataReader.getClassMetadata()
- .getClassName())
- .getPackage()
- .getName());
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
- }
- if (result.size() > 0) {
- HashSet<String> hashResult = new HashSet<String>(result);
- allResult.addAll(hashResult);
- }
- }
- if (allResult.size() > 0) {
- typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
- } else {
- throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return typeAliasesPackage;
- }
- /**
- * 创建数据源
- * @return
- */
- @Bean("masterDataSource")
- @Primary
- @Qualifier("masterDataSource")
- @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
- public DruidDataSource dataSource() {
- //注册时 使用多数据源中 的master数据源 配置
- DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
- return dataSource;
- }
- /**
- * 动态数据源配置 sqlSessionFactory
- * @return
- * @throws Exception
- */
- @Bean
- public SqlSessionFactory sqlSessionFactory() throws Exception {
- String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
- String mapperLocations = env.getProperty("mybatis.mapperLocations");
- String configLocation = env.getProperty("mybatis.configLocation");
- typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
- VFS.addImplClass(SpringBootVFS.class);
- final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
- sessionFactory.setDataSource(dynamicDataSource());
- sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
- sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
- sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
- GlobalConfig globalConfig = new GlobalConfig();
- // globalConfig.setMetaObjectHandler(new MybatisMetaObjectHandler());
- globalConfig.setSqlInjector(new MPJSqlInjector());
- sessionFactory.setGlobalConfig(globalConfig);
- //加载插件
- sessionFactory.setPlugins(mybatisPlusInterceptor(), new MPJInterceptor());
- return sessionFactory.getObject();
- }
- @Bean
- public MybatisPlusInterceptor mybatisPlusInterceptor(){
- MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
- return interceptor;
- }
- /**
- * 动态数据源配置
- * @return
- */
- @Bean(name = "dynamicDataSource")
- @Qualifier("dynamicDataSource")
- public DynamicDataSource dynamicDataSource() {
- DynamicDataSource dynamicDataSource = new DynamicDataSource();
- //配置缺省的数据源
- // 默认数据源配置 DefaultTargetDataSource
- dynamicDataSource.setDefaultTargetDataSource(dataSource());
- Map<Object, Object> targetDataSources = new HashMap<>();
- //额外数据源配置 TargetDataSources
- targetDataSources.put("master", dataSource());
- try {
- List<DbConfigModal> dbConfigModals = new ArrayList<>();
- dbConfigModals = DbInitUtil.checkDbConfig(new DbConfigModal(), "application-"+version+".yml");
- if(!CollectionUtils.isEmpty(dbConfigModals)){
- dbConfigModals.forEach(tableConfigDto -> {
- //解析处理数据连接参数
- Map<String, String> env = System.getenv();
- String dataSource = tableConfigDto.getDataSource().toUpperCase();
- String url = ParseUtil.parse(ParseUtil.mathStr_1, tableConfigDto.getDbUrl());
- String username = ParseUtil.parse(ParseUtil.mathStr_1, tableConfigDto.getDbUsername());
- String pwd = ParseUtil.parse(ParseUtil.mathStr_1, tableConfigDto.getDbPwd());
- if(!Objects.isNull(env)){
- url = StringUtils.isBlank(env.get(dataSource + "_DATASOURCE_URL")) ? url : env.get(dataSource + "_DATASOURCE_URL");
- username = StringUtils.isBlank(env.get(dataSource + "_DATASOURCE_USERNAME")) ? username : env.get(dataSource + "_DATASOURCE_USERNAME");
- pwd = StringUtils.isBlank(env.get(dataSource + "_DATASOURCE_PASSWORD")) ? pwd : env.get(dataSource + "_DATASOURCE_PASSWORD");
- }
- tableConfigDto.setDbUrl(getDbParams("DATASOURCE_URL:",url));
- tableConfigDto.setDbUsername(getDbParams("DATASOURCE_USERNAME:",username));
- tableConfigDto.setDbPwd(getDbParams("DATASOURCE_PASSWORD:",pwd));
- if(StringUtils.isNotBlank(tableConfigDto.getDataSource())){
- dynamicDataSource.testDatasource(tableConfigDto.getDataSource(),tableConfigDto.getDriverClassName(), tableConfigDto.getDbUrl(), tableConfigDto.getDbUsername(), tableConfigDto.getDbPwd());
- try {
- targetDataSources.put(tableConfigDto.getDataSource(), initDataSource(tableConfigDto));
- } catch (SQLException e) {
- log.info(" 注册数据源 SLAVE 失败 ");
- }
- }
- });
- }
- } catch (Exception e) {
- log.info(" 注册数据源 SLAVE 失败 ");
- e.printStackTrace();
- }
- dynamicDataSource.setTargetDataSources(targetDataSources);
- return dynamicDataSource;
- }
- @Nullable
- private static String getDbParams(String formatStr , String url) {
- if(StringUtils.isNotBlank(url)){
- String[] urls = url.split(formatStr);
- if(urls.length>1){
- url = urls[1];
- }
- }
- return url;
- }
- /**
- * 初始化数据源
- * @param config
- * @return
- * @throws SQLException
- */
- private DataSource initDataSource(DbConfigModal config) throws SQLException{
- DruidDataSource datasource = new DruidDataSource();
- // 基础连接信息
- datasource.setUrl(config.getDbUrl());
- datasource.setUsername(config.getDbUsername());
- datasource.setPassword(config.getDbPwd());
- datasource.setDriverClassName(config.getDriverClassName());
- // 连接池连接信息
- datasource.setInitialSize(initialSize);
- datasource.setMinIdle(minIdle);
- datasource.setMaxActive(maxActive);
- datasource.setMaxWait(maxWait);
- datasource.setConnectionErrorRetryAttempts(3);//失败后重试次数
- datasource.setBreakAfterAcquireFailure(true);//请求失败之后中断
- datasource.setPoolPreparedStatements(true); //是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
- datasource.setMaxPoolPreparedStatementPerConnectionSize(20);
- datasource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");//对于耗时长的查询sql,会受限于ReadTimeout的控制,单位毫秒
- datasource.setTestOnBorrow(true); //申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用
- datasource.setTestWhileIdle(true);//建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
- String validationQuery = "select 1 ";
- datasource.setValidationQuery(validationQuery); //用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
- datasource.setFilters("stat,wall");//属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
- datasource.setTimeBetweenEvictionRunsMillis(60000); //配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
- datasource.setMinEvictableIdleTimeMillis(180000); //配置一个连接在池中最小生存的时间,单位是毫秒,这里配置为3分钟180000
- datasource.setKeepAlive(true); //打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
- datasource.setRemoveAbandoned(true); //是否移除泄露的连接/超过时间限制是否回收。
- datasource.setRemoveAbandonedTimeout(3600); //泄露连接的定义时间(要超过最大事务的处理时间);单位为秒。这里配置为1小时
- datasource.setLogAbandoned(true); ////移除泄露连接发生是是否记录日志
- return datasource;
- }
- // =================== 事务管理器配置 ===================
- /**
- * 主数据源事务管理器
- * 用于显式指定 master 数据源的事务
- */
- @Bean("masterTransactionManager")
- @Primary
- public PlatformTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
- return new DataSourceTransactionManager(dataSource);
- }
- /**
- * 动态数据源事务管理器
- * 推荐与 @DS 注解配合使用,能感知数据源切换
- */
- @Bean("dynamicTransactionManager")
- public PlatformTransactionManager dynamicTransactionManager( @Qualifier("dynamicDataSource") DataSource dataSource) {
- return new DataSourceTransactionManager(dataSource);
- }
- }
|