由于工作交接,重写写了一遍相关文档,我这里记录一下。
该文档为工作交接,将集成改造的动态数据源依赖集成与使用说明文档。
<properties> <maven.compiler.source>1.8maven.compiler.source> <maven.compiler.target>1.8maven.compiler.target> <druid.version>1.1.21druid.version> <mybatisplus.boot.version>3.1.0mybatisplus.boot.version> <dynamic.datasource.boot.version>2.5.4dynamic.datasource.boot.version> properties>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>${mybatisplus.boot.version}version>
dependency>
<dependency> <groupId>com.baomidougroupId> <artifactId>dynamic-datasource-spring-boot-starterartifactId> <version>${dynamic.datasource.boot.version}version> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>druid-spring-boot-starterartifactId> <version>${druid.version}version> dependency>
<dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> <version>1.16.18version> <optional>trueoptional> <scope>compilescope> dependency>
0、MybatisPlusConfig
该配置类仅为排除依赖配置。
1)其中MysqlDynamicDataSourceProvider类实现了根据初始化数据源中的数据库表中数据初始化数据源,将其注入到spring容器当中。
2)PagePlugin 是mybatis分页插件。
package com.bonc.ioc.core.config; import com.bonc.ioc.core.MysqlDynamicDataSourceProvider; import com.bonc.ioc.core.page.PagePlugin; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * 配置动态数据源 */ @Configuration @Primary public class DataSourceConfig { @Value("${spring.datasource.dynamic.datasource.master.driverClassName}") private String driverClassName; @Value("${spring.datasource.dynamic.datasource.master.url}") private String url; @Value("${spring.datasource.dynamic.datasource.master.username}") private String username; @Value("${spring.datasource.dynamic.datasource.master.password}") private String password; @Bean public MysqlDynamicDataSourceProvider mysqlDynamicDataSourceProvider(){ return new MysqlDynamicDataSourceProvider(driverClassName,url,username,password); } @Bean(name = {"pagePlugin"}) @Primary public PagePlugin pagePlugin() { return new PagePlugin(); } }
2、如下所属是将数据源信息由数据库中获取并初始化。
原理是继承AbstractJdbcDataSourceProvider抽象类,实现抽象类提供接口executeStmt方法,返回自己的数据源集合。
package com.bonc.ioc.core; import com.baomidou.dynamic.datasource.provider.AbstractJdbcDataSourceProvider; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; import java.util.Map; /** * 通过Mysql数据库获取数据源信息 */ public class MysqlDynamicDataSourceProvider extends AbstractJdbcDataSourceProvider{ public static final String mysqlDriver = "com.mysql.jdbc.Driver"; // mysql数据库的驱动类 public static final String oracleDriver = "oracle.jdbc.OracleDriver"; // oracles数据库的驱动类 public static final String sql2005Driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; // sqlserver数据库的驱动类 public static final String sql2000driver = "net.sourceforge.jtds.jdbc.Driver"; // sqlserver数据库的驱动类 public MysqlDynamicDataSourceProvider(String driverClassName, String url, String username, String password) { super(driverClassName, url, username, password); } @Override protected MapexecuteStmt(Statement statement) throws SQLException { Mapmap = new HashMap<>(); ResultSet rs = statement.executeQuery("select * from facts_dbconnection"); /** * 获取信息 */ while(rs.next()){ String databaseType = rs.getString("db_type"); String driverClassName = null; if (databaseType.equals("MYSQL")) { driverClassName = mysqlDriver; } else if (databaseType.equals("ORACLE")) { driverClassName = oracleDriver; } else if (databaseType.equals("SQLServer2000")) { driverClassName = sql2005Driver; } else if(databaseType.equals("SQLServer")) { driverClassName = sql2000driver; } String url = rs.getString("db_url"); String username = rs.getString("db_username"); String password = rs.getString("db_password"); String key = rs.getString("db_name"); DataSourceProperty dataSourceProperty = new DataSourceProperty(); dataSourceProperty.setPollName(key); dataSourceProperty.setDriverClassName(driverClassName); dataSourceProperty.setUrl(url); dataSourceProperty.setUsername(username); dataSourceProperty.setPassword(password); map.put(key,dataSourceProperty); } return map; } }
3、yml动态数据源配置
spring: autoconfigure: exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 为了某些版本的springboot @SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) 无法生效 application: name: index datasource: test-while-idle: true #获取连接时候验证,会影响性能 test-on-borrow: true #空闲连接回收的时间间隔,与test-while-idle一起使用,设置5分钟 time-between-eviction-runs-millis: 300000 #连接池空闲连接的有效时间 ,设置30分钟 min-evictable-idle-time-millis: 1800000 dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master,如果读者只是单数据源只需要注释掉slave相关配置即可,这里为了方便演示master与slave保持相同 datasource: master: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://x.x.x.x:xxxx/ioc_zwzbzd?serverTimezone=UTC&useUnicode=true&allowMultiQueries=true&characterEncoding=utf-8&useSS=false&useAffectedRows=true username: xxxx password: xxxx
如下配置动态事务。
package com.bonc.ioc.core.config; import com.bonc.ioc.core.util.SpringContextHolder; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableTransactionManagement @Slf4j public class MyDataSourceTransactionManagerAutoConfiguration extends DataSourceTransactionManagerAutoConfiguration { /** * 自定义事务 * MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。 * @return */ @Bean(name = "transactionManager") public DataSourceTransactionManager transactionManagers() { log.info("-------------------- transactionManager init ---------------------"); return new DataSourceTransactionManager(SpringContextHolder.getBean("dataSource")); } }
注意!!!!!!!!
由于我们集成的方案是动态数据源单体事务控制,而不是分布式事务,故对于每一个service采取事务注解后,那么每一个方法都只能于一个数据源进行操作。
建议:可以在controller层调用多个方法,每个方法去切换数据源操作。
解决的事务冲突仅仅指单线程多次切换造成的事务与数据源冲突问题而不是分布式事务,如果打了事务注解再调用多个数据源会造成冲突,导致重大错误。
第一种方式:利用请求header部分来存储你对应的数据名称,
例如value为参数。
@DS("#header.value")
第二种方式:利用session信息里面的参数来获取
例如value为参数。
@DS("#session.value")
第三种方式:利用方法参数获取
@DS("#dbName")
方法(String dbName)
第四种方式:利用对象里面的参数来获取数据源信息
@DS("#dataSource.dbName")
方法(DataSource dataSource)
第五种直接写死
@DS("master")
达到功能:
可以扩展动态获取方式,可以使用事务
注解优先级:
在方法上的注解优先于类上的DS注解。
建议使用方式:
1、在上层中调用多个被@DS注解的方法来实现单线程多次切换。
2、在单个类上进行注解。例如在service类上注解。
可以参照该类来实现自定义匹配规则与返回内容。
com.baomidou.dynamic.datasource.processor.DsHeaderProcessor
参考动态数据源配置的匹配链配置来将自定义的匹配规则注入到容器当中
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration#dsProcessor
动态增删改查有官方文档
https://github.com/baomidou/dynamic-datasource-spring-boot-starter/wiki/Manual-Add-Remove-DataSource