最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot AOP方式實(shí)現(xiàn)多數(shù)據(jù)源切換的方法

 更新時(shí)間:2018年03月29日 10:02:19   投稿:zx  
本篇文章主要介紹了SpringBoot AOP方式實(shí)現(xiàn)多數(shù)據(jù)源切換的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

最近在做保證金余額查詢優(yōu)化,在項(xiàng)目啟動(dòng)時(shí)候需要把余額全量加載到本地緩存,因?yàn)樾枰坎樵兯序T手的保證金余額,為了不影響主數(shù)據(jù)庫的性能,考慮把這個(gè)查詢走從庫。所以涉及到需要在一個(gè)項(xiàng)目中配置多數(shù)據(jù)源,并且能夠動(dòng)態(tài)切換。經(jīng)過一番摸索,完美實(shí)現(xiàn)動(dòng)態(tài)切換,記錄一下配置方法供大家參考。

設(shè)計(jì)總體思路

Spring-Boot+AOP方式實(shí)現(xiàn)多數(shù)據(jù)源切換,繼承AbstractRoutingDataSource實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)的獲取,在service層使用注解指定數(shù)據(jù)源。

步驟

一、多數(shù)據(jù)源配置

在application.properties中,我們的配置是這樣的

#主數(shù)據(jù)源
druid.master.url=jdbc:mysql://url/masterdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
druid.master.username=xxx
druid.master.password=123
druid.master.driver-class-name=com.mysql.jdbc.Driver
druid.master.max-wait=5000
druid.master.max-active=100
druid.master.test-on-borrow=true
druid.master.validation-query=SELECT 1

#從數(shù)據(jù)源
druid.slave.url=jdbc:mysql://url/slavedb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
druid.slave.username=xxx
druid.slave.password=123
druid.slave.driver-class-name=com.mysql.jdbc.Driver
druid.slave.max-wait=5000
druid.slave.max-active=100
druid.slave.test-on-borrow=true
druid.slave.validation-query=SELECT 1

讀取配置

<!-- master數(shù)據(jù)源 -->
<bean primary="true" id="masterdb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <!-- 基本屬性 url、user、password -->
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="${druid.master.url}"/>
  <property name="username" value="${druid.master.username}"/>
  <property name="password" value="${druid.master.password}"/>
  <!-- 配置初始化最大 -->
  <property name="maxActive" value="${druid.master.max-active}"/>
  <!-- 配置獲取連接等待超時(shí)的時(shí)間 -->
  <property name="maxWait" value="${druid.master.max-wait}"/>
  <property name="validationQuery" value="${druid.master.validation-query}"/>
  <property name="testOnBorrow" value="${druid.master.test-on-borrow}"/>

</bean>

<!-- slave數(shù)據(jù)源 -->
<bean primary="true" id="slavedb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <!-- 基本屬性 url、user、password -->
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="${druid.slave.url}"/>
  <property name="username" value="${druid.slave.username}"/>
  <property name="password" value="${druid.slave.password}"/>

  <!-- 配置初始化大小、最小、最大 -->
  <property name="maxActive" value="${druid.slave.max-active}"/>
  <!-- 配置獲取連接等待超時(shí)的時(shí)間 -->
  <property name="maxWait" value="${druid.slave.max-wait}"/>
  <property name="validationQuery" value="${druid.slave.validation-query}"/>
  <property name="testOnBorrow" value="${druid.slave.test-on-borrow}"/>
</bean>

<!-- 動(dòng)態(tài)數(shù)據(jù)源,根據(jù)service接口上的注解來決定取哪個(gè)數(shù)據(jù)源 -->
<bean id="dataSource" class="datasource.DynamicDataSource">
  <property name="targetDataSources">
    <map key-type="java.lang.String">
      <entry key="slave" value-ref="slavedb"/>
      <entry key="master" value-ref="masterdb"/>
    </map>
  </property>
  <property name="defaultTargetDataSource" ref="masterdb"/>
</bean>

<!-- Spring JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="dataSource" />
</bean>

<!-- Spring事務(wù)管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
  <property name="transactionManager" ref="transactionManager"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="2" />

<!-- depositdbSqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath*:mapper-xxdb/*Mapper*.xml" />
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="xxdb.mapper"/>
  <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

二、動(dòng)態(tài)數(shù)據(jù)源

spring為我們提供了AbstractRoutingDataSource,即帶路由的數(shù)據(jù)源。繼承后我們需要實(shí)現(xiàn)它的determineCurrentLookupKey(),該方法用于自定義實(shí)際數(shù)據(jù)源名稱的路由選擇方法,由于我們將信息保存到了ThreadLocal中,所以只需要從中拿出來即可。

public class DynamicDataSource extends AbstractRoutingDataSource {
  private Logger logger = LoggerFactory.getLogger(this.getClass());

  @Override
  protected Object determineCurrentLookupKey() {
    String dataSource = JdbcContextHolder.getDataSource();
    logger.info("數(shù)據(jù)源為{}",dataSource);
    return dataSource;
  }
}

三. 數(shù)據(jù)源動(dòng)態(tài)切換類

動(dòng)態(tài)數(shù)據(jù)源切換是基于AOP的,所以我們需要聲明一個(gè)AOP切面,并在切面前做數(shù)據(jù)源切換,切面完成后移除數(shù)據(jù)源名稱。

@Aspect
@Order(1)  //設(shè)置AOP執(zhí)行順序(需要在事務(wù)之前,否則事務(wù)只發(fā)生在默認(rèn)庫中)
@Component
public class DataSourceAspect {

  private Logger logger = LoggerFactory.getLogger(this.getClass());
  //切點(diǎn)
  @Pointcut("execution(* com.xxx.service.*.*(..))")
  public void aspect() { }

  @Before("aspect()")
  private void before(JoinPoint point) {
    Object target = point.getTarget();
    String method = point.getSignature().getName();
    Class<?> classz = target.getClass();// 獲取目標(biāo)類
    Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
        .getMethod().getParameterTypes();
    try {
      Method m = classz.getMethod(method, parameterTypes);
      if (m != null && m.isAnnotationPresent(MyDataSource.class)) {
        MyDataSource data = m.getAnnotation(MyDataSource.class);
        logger.info("method :{},datasource:{}",m.getName() ,data.value().getName());
        JdbcContextHolder.putDataSource(data.value().getName());// 數(shù)據(jù)源放到當(dāng)前線程中
      }
    } catch (Exception e) {
      logger.error("get datasource error ",e);
      //默認(rèn)選擇master
      JdbcContextHolder.putDataSource(DataSourceType.Master.getName());// 數(shù)據(jù)源放到當(dāng)前線程中
    }

  }

  @AfterReturning("aspect()")
  public void after(JoinPoint point) {
    JdbcContextHolder.clearDataSource();
  }
}

四、數(shù)據(jù)源管理類

public class JdbcContextHolder {

  private final static ThreadLocal<String> local = new ThreadLocal<>();

  public static void putDataSource(String name) {
    local.set(name);
  }

  public static String getDataSource() {
    return local.get();
  }

  public static void clearDataSource() {
    local.remove();
  }
}

五、數(shù)據(jù)源注解和枚舉

我們切換數(shù)據(jù)源時(shí),一般都是在調(diào)用具體接口的方法前實(shí)現(xiàn),所以我們定義一個(gè)方法注解,當(dāng)AOP檢測到方法上有該注解時(shí),根據(jù)注解中value對應(yīng)的名稱進(jìn)行切換。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyDataSource {

  DataSourceType value();

}
public enum DataSourceType {
  // 主表
  Master("master"),
  // 從表
  Slave("slave");

  private String name;

  private DataSourceType(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

六、切點(diǎn)注解

由于我們的動(dòng)態(tài)數(shù)據(jù)源配置了默認(rèn)庫,所以如果方法是操作默認(rèn)庫的可以不需要注解,如果要操作非默認(rèn)數(shù)據(jù)源,我們需要在方法上添加@MyDataSource("數(shù)據(jù)源名稱")注解,這樣就可以利用AOP實(shí)現(xiàn)動(dòng)態(tài)切換了

@Component
public class xxxServiceImpl {
  @Resource
  private XxxMapperExt xxxMapperExt;

  @MyDataSource(value= DataSourceType.Slave)
  public List<Object> getAll(){
    return xxxMapperExt.getAll();
  }
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • ObjectInputStream 和 ObjectOutputStream 介紹_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    ObjectInputStream 和 ObjectOutputStream 介紹_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    ObjectInputStream 和 ObjectOutputStream 的作用是,對基本數(shù)據(jù)和對象進(jìn)行序列化操作支持。本文給大家詳細(xì)介紹了ObjectInputStream 和 ObjectOutputStream的相關(guān)知識(shí),感興趣的朋友一起學(xué)習(xí)吧
    2017-05-05
  • Java類和對象習(xí)題及詳細(xì)答案解析

    Java類和對象習(xí)題及詳細(xì)答案解析

    這篇文章主要介紹了Java類和對象的相關(guān)知識(shí),包括局部變量初始化、靜態(tài)方法、靜態(tài)導(dǎo)入、構(gòu)造方法、代碼塊執(zhí)行順序、toString方法重寫、類變量和靜態(tài)成員變量的訪問等,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • SpringBoot結(jié)合Redis實(shí)現(xiàn)緩存

    SpringBoot結(jié)合Redis實(shí)現(xiàn)緩存

    本文主要介紹了SpringBoot結(jié)合Redis實(shí)現(xiàn)緩存,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • SpringBoot集成WebSocket的兩種方式(JDK內(nèi)置版和Spring封裝版)

    SpringBoot集成WebSocket的兩種方式(JDK內(nèi)置版和Spring封裝版)

    這篇文章主要介紹了SpringBoot集成WebSocket的兩種方式,這兩種方式為JDK內(nèi)置版和Spring封裝版,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • SpringBoot中的@PreAuthorize注解詳解

    SpringBoot中的@PreAuthorize注解詳解

    這篇文章主要介紹了SpringBoot中的@PreAuthorize注解詳解,@PreAuthorize注解會(huì)在方法執(zhí)行前進(jìn)行權(quán)限驗(yàn)證,支持Spring?EL表達(dá)式,它是基于方法注解的權(quán)限解決方案,需要的朋友可以參考下
    2023-09-09
  • Java?數(shù)據(jù)庫連接池DBPool?介紹

    Java?數(shù)據(jù)庫連接池DBPool?介紹

    這篇文章主要給大家分享了Java?數(shù)據(jù)庫連接池DBPool?介紹,<BR>DBPool是一個(gè)高效的易配置的數(shù)據(jù)庫連接池。它除了支持連接池應(yīng)有的功能之外,還包括了一個(gè)對象池使你能夠開發(fā)一個(gè)滿足自已需求的數(shù)據(jù)庫連接池,下面一起來看看文章內(nèi)容的詳細(xì)介紹吧,需要的朋友可以參考一下
    2021-11-11
  • Java基于UDP協(xié)議的聊天室功能

    Java基于UDP協(xié)議的聊天室功能

    這篇文章主要為大家詳細(xì)介紹了Java基于UDP協(xié)議的聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • MybatisPlus創(chuàng)建時(shí)間不想用默認(rèn)值的問題

    MybatisPlus創(chuàng)建時(shí)間不想用默認(rèn)值的問題

    MybatisPlus通過FieldFill注解和MpMetaObjectHandler類支持自動(dòng)填充字段功能,特別地,可以設(shè)置字段在插入或更新時(shí)自動(dòng)填充創(chuàng)建時(shí)間和更新時(shí)間,但在特定場景下,如導(dǎo)入數(shù)據(jù)時(shí),可能需要自定義創(chuàng)建時(shí)間
    2024-09-09
  • 最新評(píng)論

    城固县| 酒泉市| 小金县| 甘洛县| 读书| 西和县| 博乐市| 高州市| 上虞市| 文水县| 遂昌县| 凉城县| 当雄县| 肥城市| 怀来县| 罗田县| 类乌齐县| 新乐市| 辉县市| 天水市| 秦安县| 神农架林区| 靖宇县| 紫阳县| 清徐县| 惠水县| 沛县| 韶山市| 梧州市| 大名县| 启东市| 浦北县| 通江县| 巩留县| 千阳县| 凤城市| 克什克腾旗| 大化| 星子县| 宜宾市| 达州市|