Springboot--多数据源读写分离和主库数据源service层事务控制
2018-03-08 16:07
344 查看
因为想研究数据库读写分离和分库分表的设计,所以就自己搭建了一套springboot+druid+mybatis+aop 实现一主多从的设计。
第一步:首先需要自定义数据源的配置项,springboot默认解析的是带前缀spring.datasource.下面的配置项,为了不冲突,就直接定义datasource.当成我们的前缀,
@ConfigurationProperties(prefix = “datasource.write”)可以用来加载指定前缀的配置项,非常方便
因为使用druid,所以需要生成datasource的时候需要指定类型。
第二步:从库的负载均衡,主要是MyAbstractRoutingDataSource这个类
第三步,从写springboot-mybatis架包的MybatisAutoConfiguration类的创建SqlSessionFactory方法,将里面的数据源换成我们自定义的AbstractRoutingDataSource
第四步骤。自定义事务MyDataSourceTransactionManagerAutoConfiguration
完整代码和单元测试:
github:https://github.com/ggj2010/javabase.git
主要的架包
第一步:首先需要自定义数据源的配置项,springboot默认解析的是带前缀spring.datasource.下面的配置项,为了不冲突,就直接定义datasource.当成我们的前缀,
@ConfigurationProperties(prefix = “datasource.write”)可以用来加载指定前缀的配置项,非常方便
因为使用druid,所以需要生成datasource的时候需要指定类型。
DataSourceBuilder.create().type(dataSourceType).build()readSize是用来定义从库的大小,有多少从库就要配置多少个从库datasource
第二步:从库的负载均衡,主要是MyAbstractRoutingDataSource这个类
第三步,从写springboot-mybatis架包的MybatisAutoConfiguration类的创建SqlSessionFactory方法,将里面的数据源换成我们自定义的AbstractRoutingDataSource
第四步骤。自定义事务MyDataSourceTransactionManagerAutoConfiguration
完整代码和单元测试:
github:https://github.com/ggj2010/javabase.git
主要的架包
<!-- jdbc driver begin--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--mybatis springboot--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!-- jdbc driver end--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>自定义数据源配置项:
#多数据源 1主2从 datasource: #从库数量 readSize: 2 # 使用druid数据源 type: com.alibaba.druid.pool.DruidDataSource #主库 write: url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf-8 username: root password: root driver-class-name: com.mysql.jdbc.Driver filters: stat maxActive: 20 initialSize: 1 maxWait: 60000 minIdle: 1 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQueryTimeout: 900000 validationQuery: SELECT SYSDATE() from dual testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxOpenPreparedStatements: 20 read1: url: jdbc:mysql://localhost:3306/slave1?useUnicode=true&characterEncoding=utf-8 username: root password: root driver-class-name: com.mysql.jdbc.Driver filters: stat maxActive: 20 initialSize: 1 maxWait: 60000 minIdle: 1 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQueryTimeout: 900000 validationQuery: SELECT SYSDATE() from dual testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxOpenPreparedStatements: 20 read2: url: jdbc:mysql://localhost:3306/slave2?useUnicode=true&characterEncoding=utf-8 username: root password: root driver-class-name: com.mysql.jdbc.Driver filters: stat maxActive: 20 initialSize: 1 maxWait: 60000 minIdle: 1 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQueryTimeout: 900000 validationQuery: SELECT SYSDATE() from dual testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxOpenPreparedStatements: 20解析配置项:
@Configuration @Slf4j public class DataSourceConfiguration { @Value("${datasource.type}") private Class<? extends DataSource> dataSourceType; @Bean(name = "writeDataSource") @Primary @ConfigurationProperties(prefix = "datasource.write") public DataSource writeDataSource() { log.info("-------------------- writeDataSource init ---------------------"); return DataSourceBuilder.create().type(dataSourceType).build(); } /** * 有多少个从库就要配置多少个 * @return */ @Bean(name = "readDataSource1") @ConfigurationProperties(prefix = "datasource.read1") public DataSource readDataSourceOne() { log.info("-------------------- readDataSourceOne init ---------------------"); return DataSourceBuilder.create().type(dataSourceType).build(); } @Bean(name = "readDataSource2") @ConfigurationProperties(prefix = "datasource.read2") public DataSource readDataSourceTwo() { log.info("-------------------- readDataSourceTwo init ---------------------"); return DataSourceBuilder.create().type(dataSourceType).build(); } }重写SqlSessionFactory
@Configuration @AutoConfigureAfter({ DataSourceConfiguration.class }) @Slf4j public class MybatisConfiguration extends MybatisAutoConfiguration { @Value("${datasource.readSize}") private String dataSourceSize; @Bean public SqlSessionFactory sqlSessionFactorys() throws Exception { log.info("-------------------- 重载父类 sqlSessionFactory init ---------------------"); return super.sqlSessionFactory(roundRobinDataSouceProxy()); } /** * 有多少个数据源就要配置多少个bean * @return */ @Bean public AbstractRoutingDataSource roundRobinDataSouceProxy() { int size = Integer.parseInt(dataSourceSize); MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource(size); Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); DataSource writeDataSource = SpringContextHolder.getBean("writeDataSource"); // 写 targetDataSources.put(DataSourceType.write.getType(), SpringContextHolder.getBean("writeDataSource")); for (int i = 0; i < size; i++) { targetDataSources.put(i, SpringContextHolder.getBean("readDataSource" + (i + 1))); } proxy.setDefaultTargetDataSource(writeDataSource); proxy.setTargetDataSources(targetDataSources); return proxy; } }本地线程全局变量
public class DataSourceContextHolder { private static final ThreadLocal<String> local = new ThreadLocal<String>(); public static ThreadLocal<String> getLocal() { return local; } /** * 读可能是多个库 */ public static void read() { local.set(DataSourceType.read.getType()); } /** * 写只有一个库 */ public static void write() { local.set(DataSourceType.write.getType()); } public static String getJdbcType() { return local.get(); } }多数据源切换
public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource { private final int dataSourceNumber; private AtomicInteger count = new AtomicInteger(0); public MyAbstractRoutingDataSource(int dataSourceNumber) { this.dataSourceNumber = dataSourceNumber; } @Override protected Object determineCurrentLookupKey() { String typeKey = DataSourceContextHolder.getJdbcType(); if (typeKey.equals(DataSourceType.write.getType())) return DataSourceType.write.getType(); // 读 简单负载均衡 int number = count.getAndAdd(1); int lookupKey = number % dataSourceNumber; return new Integer(lookupKey); } }enum类型
public enum DataSourceType { read("read", "从库"), write("write", "主库"); @Getter private String type; @Getter private String name; DataSourceType(String type, String name) { this.type = type; this.name = name; } }aop拦截设置本地线程变量
@Aspect @Component @Slf4j public class DataSourceAop { @Before("execution(* com.ggj.encrypt.modules.*.dao..*.find*(..)) or execution(* com.ggj.encrypt.modules.*.dao..*.get*(..))") public void setReadDataSourceType() { DataSourceContextHolder.read(); log.info("dataSource切换到:Read"); } @Before("execution(* com.ggj.encrypt.modules.*.dao..*.insert*(..)) or execution(* com.ggj.encrypt.modules.*.dao..*.update*(..))") public void setWriteDataSourceType() { DataSourceContextHolder.write(); log.info("dataSource切换到:write"); } }自定义事务
@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("roundRobinDataSouceProxy")); } }
相关文章推荐
- springboot多数据源读写分离和主库数据源service层事务控制
- springboot多数据源读写分离和主库数据源service层事务控制
- springboot+mybatis解决多数据源切换事务控制不生效的问题
- SpringBoot系列七:SpringBoot 整合 MyBatis(配置 druid 数据源、配置 MyBatis、事务控制、druid 监控)
- spring-boot入门(七)atomikos+druid+多数据源下的分布式事务配置
- Springboot Mybatis MySQL读写分离及事物配置
- Spring Boot多数据源及其事务管理配置方法
- Spring Boot Vue Admin 前后端完全分离的权限控制模版
- Spring-boot实现数据库读写分离
- Spring Boot : 集成mybatis、pagehelper插件、开启事务、druid数据源配置、mybatis-generator(十一)
- Springboot 事务管理控制
- spring 配置双数据源并读写分离
- Spring Boot 集成Mybatis实现主从(多数据源)分离方案
- Springboot+Mybatis+Druid 实现多数据源,与事务管理
- Spring Boot 集成Mybatis实现主从(多数据源)分离方案示例
- spring boot 多数据源的XA事务(Druid+Atomikos)
- Spring Boot(二):整合 JPA 及 事务控制
- spring4+mybatis3+atomikos3.9多数据源分布式事务控制
- SpringBoot整合Mybatis,多数据源,事务,支持java -jar 启动.
- 3分钟搞定SpringBoot+Mybatis+druid多数据源和分布式事务