<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          springboot如何配置多數(shù)據(jù)源及單庫(kù)事務(wù)控制?

          共 6383字,需瀏覽 13分鐘

           ·

          2020-11-03 23:14


          1.背景介紹

          通過(guò)springboot操作mysql數(shù)據(jù)庫(kù),但是在實(shí)際業(yè)務(wù)場(chǎng)景中,數(shù)據(jù)量迅速增長(zhǎng),一個(gè)庫(kù)一個(gè)表已經(jīng)滿(mǎn)足不了我們的需求的時(shí)候,我們就會(huì)考慮分庫(kù)分表的操作,在springboot中如何實(shí)現(xiàn)多數(shù)據(jù)源,動(dòng)態(tài)數(shù)據(jù)源切換,讀寫(xiě)分離等操作。


          2.所需依賴(lài)


          #父級(jí)項(xiàng)目依賴(lài)
          <parent>
          ????<groupId>org.springframework.bootgroupId>
          ????<artifactId>spring-boot-starter-parentartifactId>
          ????<version>2.1.5.RELEASEversion>
          ??parent>
          ??#其他依賴(lài)
          ??<dependencies>
          ????
          ????
          ????<dependency>
          ??????<groupId>org.springframework.bootgroupId>
          ??????<artifactId>spring-boot-starter-webartifactId>
          ????dependency>
          ????<dependency>
          ??????<groupId>org.springframework.bootgroupId>
          ??????<artifactId>spring-boot-starter-testartifactId>
          ????dependency>
          ????<dependency>
          ??????<groupId>org.springframework.bootgroupId>
          ??????<artifactId>spring-boot-starter-aopartifactId>
          ????dependency>
          ????<dependency>
          ??????<groupId>com.alibabagroupId>
          ??????<artifactId>druidartifactId>
          ??????<version>1.0.2version>
          ????dependency>
          ????<dependency>
          ??????<groupId>mysqlgroupId>
          ??????<artifactId>mysql-connector-javaartifactId>
          ??????<scope>runtimescope>
          ????dependency>
          ????
          ????<dependency>
          ??????<groupId>org.mybatis.spring.bootgroupId>
          ??????<artifactId>mybatis-spring-boot-starterartifactId>
          ??????<version>1.3.1version>
          ????dependency>
          ????
          ????<dependency>
          ??????<groupId>org.aspectjgroupId>
          ??????<artifactId>aspectjweaverartifactId>
          ????dependency>
          ????<dependency>
          ??????<groupId>org.projectlombokgroupId>
          ??????<artifactId>lombokartifactId>
          ????dependency>
          ??dependencies>


          yml文件中配置


          #多數(shù)據(jù)源 12
          datasource:
          ??#從庫(kù)數(shù)量
          ??readSize: 2
          ??# 使用druid數(shù)據(jù)源
          ??type: com.alibaba.druid.pool.DruidDataSource
          ??#主庫(kù)
          ??write:
          ????url:?jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=GMT%2B8
          ????username: root
          ????password: root
          ????driver-class-name: com.mysql.cj.jdbc.Driver
          ????filters:?stat
          ????maxActive: 20
          ????initialSize: 1
          ????maxWait:?60000
          ????minIdle: 1
          ????timeBetweenEvictionRunsMillis:?60000
          ????minEvictableIdleTimeMillis:?300000
          ????validationQueryTimeout:?900000
          ????validationQuery: SELECT 1
          ????testWhileIdle: true
          ????testOnBorrow:?false
          ????testOnReturn: false
          ????poolPreparedStatements:?true
          ????maxOpenPreparedStatements:?20
          ??read1:
          ????url:?jdbc:mysql://localhost:3306/wb?characterEncoding=utf-8&serverTimezone=GMT%2B8
          ????username: root
          ????password: root
          ????driver-class-name: com.mysql.cj.jdbc.Driver
          ????filters:?stat
          ????maxActive: 20
          ????initialSize: 1
          ????maxWait:?60000
          ????minIdle: 1
          ????timeBetweenEvictionRunsMillis:?60000
          ????minEvictableIdleTimeMillis:?300000
          ????validationQueryTimeout:?900000
          ????validationQuery: SELECT 1
          ????testWhileIdle: true
          ????testOnBorrow:?false
          ????testOnReturn: false
          ????poolPreparedStatements:?true
          ????maxOpenPreparedStatements:?20
          ??read2:
          ????url:?jdbc:mysql://localhost:3306/sys?characterEncoding=utf-8&serverTimezone=GMT%2B8
          ????username: root
          ????password: root
          ????driver-class-name: com.mysql.cj.jdbc.Driver
          ????filters:?stat
          ????maxActive: 20
          ????initialSize: 1
          ????maxWait:?60000
          ????minIdle: 1
          ????timeBetweenEvictionRunsMillis:?60000
          ????minEvictableIdleTimeMillis:?300000
          ????validationQueryTimeout:?900000
          ????validationQuery: SELECT 1
          ????testWhileIdle: true
          ????testOnBorrow:?false
          ????testOnReturn: false
          ????poolPreparedStatements:?true
          ????maxOpenPreparedStatements:?20


          1.自定義注解標(biāo)簽TargetDataSource,主要用于在接口處通過(guò)注解來(lái)切換數(shù)據(jù)源


          package?com.lenovo.annotation;
          import?com.lenovo.meiju.DataSourceType;
          import?java.lang.annotation.*;
          @Target({ElementType.METHOD, ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public?@interface?TargetDataSource {
          ??//默認(rèn)使用寫(xiě)庫(kù)
          ????DataSourceType value()?default?DataSourceType.write;
          }


          2.定義切面DataSourceAop

          通過(guò)獲取接口處的注解,獲取注解所需要切換的數(shù)據(jù)源名稱(chēng),從而來(lái)切換該數(shù)據(jù)源


          package?com.lenovo.interceptor;
          import?com.lenovo.annotation.TargetDataSource;
          import?com.lenovo.config.DataSourceContextHolder;
          import?lombok.extern.slf4j.Slf4j;
          import?org.aspectj.lang.JoinPoint;
          import?org.aspectj.lang.annotation.After;
          import?org.aspectj.lang.annotation.Aspect;
          import?org.aspectj.lang.reflect.MethodSignature;
          import?org.springframework.stereotype.Component;
          import?org.aspectj.lang.annotation.Before;
          import?java.lang.reflect.Method;
          @Aspect
          @Component
          @Slf4j
          public?class?DataSourceAop?{
          ????@Before("@annotation(targetDataSource)")
          ????public?void?setWriteDataSourceType(JoinPoint point, TargetDataSource targetDataSource)?{
          ????????//獲得當(dāng)前訪(fǎng)問(wèn)的class
          ????????Class className = point.getTarget().getClass();
          ????????//獲得訪(fǎng)問(wèn)的方法名
          ????????String methodName = point.getSignature().getName();
          ????????//得到方法的參數(shù)的類(lèi)型
          ????????Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
          ????????//獲取默認(rèn)的數(shù)據(jù)源名稱(chēng)
          ????????String dataSource = DataSourceContextHolder.DEFAULT_DS;
          ????????try?{
          ????????????// 得到訪(fǎng)問(wèn)的方法對(duì)象
          ????????????Method method = className.getMethod(methodName, argClass);
          ????????????// 判斷是否存在@注解
          ????????????if?(method.isAnnotationPresent(TargetDataSource.class)) {
          ????????????????TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
          ????????????????// 取出注解中的數(shù)據(jù)源名
          ????????????????dataSource = annotation.value().getType();
          ????????????}
          ????????} catch?(Exception e) {
          ????????????e.printStackTrace();
          ????????}
          ????????// 切換數(shù)據(jù)源
          ????????DataSourceContextHolder.setJdbcType(dataSource);
          ????}
          ????@After("@annotation(targetDataSource)")
          ????public?void?afterSwitchDS(JoinPoint point,TargetDataSource targetDataSource){
          ????????DataSourceContextHolder.clearDB();
          ????}
          }


          3.設(shè)置本地線(xiàn)程全局變量


          package?com.lenovo.config;
          import?lombok.extern.slf4j.Slf4j;
          @Slf4j
          public?class?DataSourceContextHolder?{
          ????private?static?final?ThreadLocal local = new?ThreadLocal();
          ????public?static?final?String DEFAULT_DS = "write";//默認(rèn)數(shù)據(jù)源
          ????public?static?ThreadLocal getLocal()?{
          ????????return?local;
          ????}
          ????public?static?String getJdbcType()?{
          ????????return?local.get();
          ????}
          ????public?static?void?setJdbcType(String dbType)?{
          ????????log.info("dataSource切換到:"+dbType);
          ????????local.set(dbType);
          ????}
          ????// 清除數(shù)據(jù)源名
          ????public?static?void?clearDB()?{
          ????????local.remove();
          ????}
          }


          4.數(shù)據(jù)庫(kù)配置:解析application-pro.yml文件

          通過(guò)@ConfigurationProperties注解 獲取配置文件屬性自動(dòng)配置,綁定其屬性

          通過(guò)@Bean注解,申請(qǐng)實(shí)例對(duì)象


          package?com.lenovo.config;
          import?com.lenovo.meiju.DataSourceType;
          import?lombok.extern.slf4j.Slf4j;
          import?org.apache.ibatis.session.SqlSessionFactory;
          import?org.mybatis.spring.SqlSessionFactoryBean;
          import?org.springframework.beans.factory.annotation.Value;
          import?org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
          import?org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
          import?org.springframework.context.annotation.Bean;
          import?org.springframework.context.annotation.Configuration;
          import?org.springframework.context.annotation.Import;
          import?org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
          import?org.springframework.transaction.annotation.EnableTransactionManagement;
          import?javax.annotation.Resource;
          import?javax.sql.DataSource;
          import?java.util.ArrayList;
          import?java.util.HashMap;
          import?java.util.List;
          import?java.util.Map;
          @Slf4j
          @Configuration
          @ConditionalOnClass({EnableTransactionManagement.class})
          @Import({ DataBaseConfiguration.class})
          public?class?MybatisConfiguration?{
          ????@Value("${datasource.type}")
          ????private?Class dataSourceType;
          ????@Value("${datasource.readSize}")
          ????private?String dataSourceSize;
          ????@Resource(name = "writeDataSource")
          ????private?DataSource dataSource;
          ????@Resource(name = "readDataSource1")
          ????private?DataSource read1DataSources;
          ????@Resource(name = "readDataSource2")
          ????private?DataSource read2DataSources;
          ????List readDataSources;
          ????@Bean
          ????@ConditionalOnMissingBean
          ????public?SqlSessionFactory sqlSessionFactory()?throws?Exception {
          ????????SqlSessionFactoryBean sqlSessionFactoryBean = new?SqlSessionFactoryBean();
          ????????sqlSessionFactoryBean.setDataSource(roundRobinDataSouceProxy());
          ????????return?sqlSessionFactoryBean.getObject();
          ????}
          ????/**
          ?????* 設(shè)置默認(rèn)數(shù)據(jù)庫(kù),其他數(shù)據(jù)源
          ?????* @return
          ?????*/

          ????@Bean(name = "dynamicDataSource")
          ????public?AbstractRoutingDataSource roundRobinDataSouceProxy()?{
          ????????int?size = Integer.parseInt(dataSourceSize);
          ????????MyAbstractRoutingDataSource proxy = new?MyAbstractRoutingDataSource(size);
          ????????Map targetDataSources = new?HashMap();
          ????????// DataSource writeDataSource = SpringContextHolder.getBean("writeDataSource");
          ????????// 寫(xiě)
          ????????targetDataSources.put(DataSourceType.write.getType(),dataSource);
          ????????// targetDataSources.put(DataSourceType.read.getType(),readDataSource);
          ????????//多個(gè)讀數(shù)據(jù)庫(kù)時(shí)
          ????????readDataSources=new?ArrayList();
          ????????readDataSources.add(read1DataSources);
          ????????readDataSources.add(read2DataSources);
          ????????for?(int?i = 0; i < size; i++) {
          ????????????targetDataSources.put(i, readDataSources.get(i));
          ????????}
          ????????proxy.setDefaultTargetDataSource(dataSource);
          ????????proxy.setTargetDataSources(targetDataSources);
          ????????return?proxy;
          ????}
          }


          5.通過(guò)枚舉法區(qū)分讀寫(xiě)庫(kù)標(biāo)識(shí)


          package com.lenovo.meiju;
          public?enum??DataSourceType {
          ????read("read", "從庫(kù)"),
          ????write("write", "主庫(kù)");
          ????private?String?type;
          ????private?String?name;
          ????public?String?getType() {
          ????????return?type;
          ????}
          ????public?void?setType(String?type) {
          ????????this.type = type;
          ????}
          ????public?String?getName() {
          ????????return?name;
          ????}
          ????public?void?setName(String?name) {
          ????????this.name = name;
          ????}
          ????DataSourceType(String?type, String?name) {
          ????????this.type = type;
          ????????this.name = name;
          ????}
          }


          6.通過(guò)繼承AbstractRoutingDataSource實(shí)現(xiàn)其動(dòng)態(tài)選擇數(shù)據(jù)源


          package?com.lenovo.config;
          import?com.lenovo.meiju.DataSourceType;
          import?org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
          import?java.util.concurrent.atomic.AtomicInteger;
          public?class?MyAbstractRoutingDataSource?extends?AbstractRoutingDataSource?{
          ????//配置的讀庫(kù)數(shù)量
          ????private?final?int?dataSourceNumber;
          ????private?AtomicInteger count = new?AtomicInteger(0);
          ????/**
          ????* @dataSourceNumber??從庫(kù)的數(shù)量
          ????**/

          ????public?MyAbstractRoutingDataSource(int?dataSourceNumber)?{
          ????????this.dataSourceNumber = dataSourceNumber;
          ????}
          ????@Override
          ????protected?Object determineCurrentLookupKey()?{
          ????????//獲取通過(guò)aop設(shè)置的數(shù)據(jù)源名稱(chēng)
          ????????String typeKey = DataSourceContextHolder.getJdbcType();
          ????????if?(typeKey==null){//未加注解 默認(rèn)使用主庫(kù)
          ????????????typeKey=DataSourceType.write.getType();
          ????????}
          ????????//如果是當(dāng)前數(shù)據(jù)源是默認(rèn)主庫(kù),直接返回主庫(kù)
          ????????if?(typeKey.equals(DataSourceType.write.getType())){
          ????????????return?DataSourceType.write.getType();
          ????????}
          ????????//從庫(kù) 讀 簡(jiǎn)單負(fù)載均衡(輪詢(xún))
          ????????int?number = count.getAndAdd(1);
          ????????int?lookupKey = number % dataSourceNumber;
          ????????return?new?Integer(lookupKey);
          ????//如果不進(jìn)行負(fù)載,直接指定數(shù)據(jù)源的話(huà),則可以這邊修改
          ????}
          }


          7.配置mybatis


          package?com.lenovo.config;
          import?com.lenovo.meiju.DataSourceType;
          import?lombok.extern.slf4j.Slf4j;
          import?org.apache.ibatis.session.SqlSessionFactory;
          import?org.mybatis.spring.SqlSessionFactoryBean;
          import?org.springframework.beans.factory.annotation.Value;
          import?org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
          import?org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
          import?org.springframework.context.annotation.Bean;
          import?org.springframework.context.annotation.Configuration;
          import?org.springframework.context.annotation.Import;
          import?org.springframework.jdbc.datasource.DataSourceTransactionManager;
          import?org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
          import?org.springframework.transaction.PlatformTransactionManager;
          import?org.springframework.transaction.annotation.EnableTransactionManagement;
          import?javax.annotation.Resource;
          import?javax.sql.DataSource;
          import?java.util.ArrayList;
          import?java.util.HashMap;
          import?java.util.List;
          import?java.util.Map;
          @Slf4j
          @Configuration
          @ConditionalOnClass({EnableTransactionManagement.class})
          @Import({ DataBaseConfiguration.class})
          public?class?MybatisConfiguration?{
          ????@Value("${datasource.type}")
          ????private?Class dataSourceType;
          ????@Value("${datasource.readSize}")
          ????private?String dataSourceSize;
          ????@Resource(name = "writeDataSource")
          ????private?DataSource dataSource;
          ????@Resource(name = "readDataSource1")
          ????private?DataSource read1DataSources;
          ????@Resource(name = "readDataSource2")
          ????private?DataSource read2DataSources;
          ????List readDataSources;
          ????@Bean
          ????@ConditionalOnMissingBean
          ????public?SqlSessionFactory sqlSessionFactory()?throws?Exception {
          ????????SqlSessionFactoryBean sqlSessionFactoryBean = new?SqlSessionFactoryBean();
          ????????sqlSessionFactoryBean.setDataSource(roundRobinDataSouceProxy());
          ????????return?sqlSessionFactoryBean.getObject();
          ????}
          ????/**
          ?????* 設(shè)置默認(rèn)數(shù)據(jù)庫(kù),其他數(shù)據(jù)源
          ?????* @return
          ?????*/

          ????@Bean(name = "dynamicDataSource")
          ????public?AbstractRoutingDataSource roundRobinDataSouceProxy()?{
          ????????int?size = Integer.parseInt(dataSourceSize);
          ????????MyAbstractRoutingDataSource proxy = new?MyAbstractRoutingDataSource(size);
          ????????Map targetDataSources = new?HashMap();
          ????????// DataSource writeDataSource = SpringContextHolder.getBean("writeDataSource");
          ????????// 寫(xiě)
          ????????targetDataSources.put(DataSourceType.write.getType(),dataSource);
          ????????// targetDataSources.put(DataSourceType.read.getType(),readDataSource);
          ????????//多個(gè)讀數(shù)據(jù)庫(kù)時(shí)
          ????????readDataSources=new?ArrayList();
          ????????readDataSources.add(read1DataSources);
          ????????readDataSources.add(read2DataSources);
          ????????for?(int?i = 0; i < size; i++) {
          ????????????targetDataSources.put(i, readDataSources.get(i));
          ????????}
          ????????proxy.setDefaultTargetDataSource(dataSource);
          ????????proxy.setTargetDataSources(targetDataSources);
          ????????return?proxy;
          ????}
          ????//配置Transaction,統(tǒng)一管理否則事務(wù)失效
          ????@Bean(name="transactionManager")
          ????public?PlatformTransactionManager transactionManager(){
          ????????return?new?DataSourceTransactionManager(roundRobinDataSouceProxy());
          ????}
          }


          8.數(shù)據(jù)源聲明


          package?com.lenovo.config;
          import?lombok.extern.slf4j.Slf4j;
          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?javax.sql.DataSource;
          @Slf4j
          @Configuration
          public class DataBaseConfiguration {
          ????@Value("${datasource.type}")
          ????private?Classextends?DataSource> dataSourceType;
          ????@Bean(name?= "writeDataSource")
          ????@Primary
          ????@ConfigurationProperties(prefix = "datasource.write")
          ????public DataSource writeDataSource() {
          ????????return?DataSourceBuilder.create().type(dataSourceType).build();
          ????}
          ????/**
          ?????* 有多少個(gè)從庫(kù)就要配置多少個(gè)
          ?????* @return
          ?????*/

          ????@Bean(name?= "readDataSource1")
          ????@ConfigurationProperties(prefix = "datasource.read1")
          ????public DataSource readDataSourceOne() {
          ????????return?DataSourceBuilder.create().type(dataSourceType).build();
          ????}
          ????@Bean(name?= "readDataSource2")
          ????@ConfigurationProperties(prefix = "datasource.read2")
          ????public DataSource readDataSourceTwo() {
          ????????return?DataSourceBuilder.create().type(dataSourceType).build();
          ????}
          }



          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青娱乐在线性爱视频 | 亚洲AV秘 无码 18日韩 | 青青草激情 | 成人免费A片 | 免费 69视频 |