1. 程式人生 > >【教程】Spring+Mybatis環境配置多資料來源

【教程】Spring+Mybatis環境配置多資料來源

一、簡要概述

在做專案的時候遇到需要從兩個資料來源獲取資料,專案使用的Spring + Mybatis環境,看到網上有一些關於多資料來源的配置,自己也整理學習一下,然後自動切換實現從不同的資料來源獲取資料功能。

二、程式碼詳解

2.1 DataSourceConstants 資料來源常量類

/**
 * 資料來源名稱常量類
 * 對應 application.xml 中 bean multipleDataSource
 * @author:dufy
 * @version:1.0.0
 * @date 2018/12/17
 */
public class DataSourceConstants
{ /** * 資料來源1,預設資料來源配置 */ public static final String DATASOURCE_1 = "dataSource1"; /** * 資料來源2 */ public static final String DATASOURCE_2 = "dataSource2"; }

2.2 DataSourceType 自定義資料來源註解

/**
 * 自定義資料來源型別註解
 *
 * @author:dufy
 * @version:1.0.0
 * @date 2018/12/17
 */

@Target
(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSourceType { String value() default DataSourceConstants.DATASOURCE_1; }

2.3 MultipleDataSource 多資料來源配置類

MultipleDataSource 繼承 AbstractRoutingDataSource 類,為什麼繼承這個類就可以了?請看 第五章 :實現原理。

import org.springframework.
jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 自定義多資料來源配置類 * * @author:dufy * @version:1.0.0 * @date 2018/12/17 */ public class MultipleDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<String>(); /** * 設定資料來源 * @param dataSource 資料來源名稱 */ public static void setDataSource(String dataSource){ dataSourceHolder.set(dataSource); } /** * 獲取資料來源 * @return */ public static String getDatasource() { return dataSourceHolder.get(); } /** * 清除資料來源 */ public static void clearDataSource(){ dataSourceHolder.remove(); } @Override protected Object determineCurrentLookupKey() { return dataSourceHolder.get(); } }

2.4 MultipleDataSourceAop 多資料來源自動切換通知類

注意:請設定 @Order(0)。否則可能出現 資料來源切換失敗問題! 因為要在事務開啟之前就進行判斷,並進行切換資料來源!

/**
 * 多資料來源自動切換通知類<br>
 * <p>
 * 首先判斷當前類是否被該DataSourceType註解進行註釋,如果沒有指定註解,則採用預設的資料來源配置; <br>
 * 如果有,則讀取註解中的value值,將資料來源切到value指定的資料來源
 *
 * @author:dufy
 * @version:1.0.0
 * @date 2018/12/17
 */

@Aspect    // for aop
@Component // for auto scan
@Order(0)  // execute before @Transactional
public class MultipleDataSourceAop {

    private final Logger logger = Logger.getLogger(MultipleDataSourceAop.class);

    /**
     * 攔截 com.**.servicee中所有的方法,根據配置情況進行資料來源切換
     * com.jiuling.tz.service
     * com.jiuling.web.service
     * @param joinPoint
     * @throws Throwable
     */
    @Before("execution(* com.dufy.*.service.*.*(..))")
    public void changeDataSource(JoinPoint joinPoint) throws Throwable {

        try {
            // 攔截的實體類,就是當前正在執行的service
            Class<?> clazz = joinPoint.getTarget().getClass();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            // 提取目標物件方法註解和型別註解中的資料來源標識
            Class<?>[] types = method.getParameterTypes();
            if (clazz.isAnnotationPresent(DataSourceType.class)) {
                DataSourceType source = clazz.getAnnotation(DataSourceType.class);
                MultipleDataSource.setDataSource(source.value());
                logger.info("Service Class 資料來源切換至--->" + source.value());
            }

            Method m = clazz.getMethod(method.getName(), types);
            if (m != null && m.isAnnotationPresent(DataSourceType.class)) {
                DataSourceType source = m.getAnnotation(DataSourceType.class);
                MultipleDataSource.setDataSource(source.value());
                logger.info("Service Method 資料來源切換至--->" + source.value());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 方法結束後
     */
    @After("execution(* com.dufy.*.service.*.*(..))")
    public void afterReturning() throws Throwable {
        try {
            MultipleDataSource.clearDataSource();
            logger.debug("資料來源已移除!");
        } catch (Exception e) {
            e.printStackTrace();
            logger.debug("資料來源移除報錯!");
        }

    }
}

三、配置詳情

applicationContext.xml 中配置詳情

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
    http://www.springframework.org/schema/cache
    http://www.springframework.org/schema/cache/spring-cache.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 自動掃描包 ,將帶有註解的類 納入spring容器管理 -->
    <context:component-scan base-package="com.dufy"></context:component-scan>

    <!-- 引入配置檔案 -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath*:jdbc.properties</value>
            </list>
        </property>
    </bean>

    <!-- dataSource1 配置 -->
    <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 基本屬性 url、user、password -->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${ds.initialSize}"/>
        <property name="minIdle" value="${ds.minIdle}"/>
        <property name="maxActive" value="${ds.maxActive}"/>

        <!-- 配置獲取連線等待超時的時間 -->
        <property name="maxWait" value="${ds.maxWait}"/>

        <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${ds.timeBetweenEvictionRunsMillis}"/>

        <!-- 配置一個連線在池中最小生存的時間,單位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${ds.minEvictableIdleTimeMillis}"/>

        <property name="validationQuery" value="SELECT 'x'"/>
        <property name="testWhileIdle" value="true"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>

        <!-- 開啟PSCache,並且指定每個連線上PSCache的大小 -->
        <property name="poolPreparedStatements" value="false"/>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>

        <!-- 配置監控統計攔截的filters -->
        <property name="filters" value="stat"/>
    </bean>

    <!-- dataSource2 配置-->
    <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 基本屬性 url、user、password -->
        <property name="url" value="${jd.jdbc.url}"/>
        <property name="username" value="${jd.jdbc.username}"/>
        <property name="password" value="${jd.jdbc.password}"/>
		<!-- 其他配置省略 -->
    </bean>


    <!--多資料來源配置-->
    <bean id="multipleDataSource" class="com.jiuling.core.ds.MultipleDataSource">
        <property name="defaultTargetDataSource" ref="dataSource1" />
        <property name="targetDataSources">
            <map key-type = "java.lang.String">
                <entry key="dataSource1" value-ref="dataSource1"/>
                <entry key="dataSource2" value-ref="dataSource2"/>
                <!-- 這裡還可以加多個dataSource -->
            </map>
        </property>
    </bean>

    <!-- mybatis檔案配置,掃描所有mapper檔案 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="multipleDataSource"
          p:configLocation="classpath:mybatis-config.xml"
          p:mapperLocations="classpath:com/dufy/*/dao/*.xml"/>

    <!-- spring與mybatis整合配置,掃描所有dao -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="com.dufy.*.dao"
          p:sqlSessionFactoryBeanName="sqlSessionFactory"/>

    <!-- 對dataSource 資料來源進行事務管理 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="multipleDataSource"/>

    <!-- 事務管理 通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 對insert,update,delete 開頭的方法進行事務管理,只要有異常就回滾 -->
            <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
            <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
            <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
            <!-- select,count開頭的方法,開啟只讀,提高資料庫訪問效能 -->
            <tx:method name="select*" read-only="true"/>
            <tx:method name="count*" read-only="true"/>
            <!-- 對其他方法 使用預設的事務管理 -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- 事務 aop 配置 -->
    <aop:config>
       <aop:pointcut id="serviceMethods" expression="execution(* com.dufy.*.service..*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
    </aop:config>

    <!-- 配置使Spring採用CGLIB代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <!-- 啟用對事務註解的支援 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
  
</beans>

jdbc.properties 配置內容

##-------------mysql資料庫連線配置 ---------------------###
# dataSource1
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.1.110:3306/jdsc?useUnicode=true&characterEncoding=utf-8
jdbc.username=test
jdbc.password=123456

#配置初始化大小、最小、最大
ds.initialSize=1
ds.minIdle=1
ds.maxActive=20
#配置獲取連線等待超時的時間 
ds.maxWait=60000

#配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
ds.timeBetweenEvictionRunsMillis=60000

#配置一個連線在池中最小生存的時間,單位是毫秒
ds.minEvictableIdleTimeMillis=300000

# dataSource2
jd.jdbc.url=jdbc:mysql://192.168.1.120:3306/jdsc?useUnicode=true&characterEncoding=utf-8
jd.jdbc.username=root
jd.jdbc.password=123456

四、測試切換

注意:測試服務類的包路徑,因為只有被AOP攔截的到的指定的Service才會進行資料來源的切換。

package com.dufy.web.service.impl.Data1ServiceImpl;

/**
 * 使用 dataSourc1 ,配置dataSourc1的資料來源
 * @author:duf
 * @version:1.0.0
 * @date 2018/12/17
 */
@Service
@DataSourceType(value = DataSourceConstants.DATASOURCE_1)
public class Data1ServiceImpl implements Data1Service {

    @Resource
    private Data1Mapper data1Mapper;

    @Override
    public List<String> selectCaseByUpdateTime(String name) {
        List<String> data1 = data1Mapper.selectData1(name);
        return data1;
    }

}

package com.dufy.web.service.impl.Data2ServiceImpl;
/**
 * 使用 dataSourc2 ,配置dataSourc2的資料來源
 * @author:duf
 * @version:1.0.0
 * @date 2018/12/17
 */
@Service
@DataSourceType(value = DataSourceConstants.DATASOURCE_2)
public class Data2ServiceImpl implements Data2Service {

    @Resource
    private Data2Mapper data2Mapper;

    @Override
    public List<String> selectCaseByUpdateTime(String name) {
        List<String> data2 = data2Mapper.selectData2(name);
        return data2;
    }

}

通過測試後發現,兩個Service伺服器分別呼叫自己的配置的資料來源進行資料的獲取!

五、實現原理

基於AbstractRoutingDataSource 實現 多資料來源配置,通過AOP來進行資料來源的靈活切換。AOP的相關原理這裡不做說明,就簡單說一下 AbstractRoutingDataSource ,它是如何切換資料來源的!

首先我們繼承 AbstractRoutingDataSource 它是一個抽象類,然後要實現它裡面的一個抽象方法。

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean 

實現了InitializingBean,InitializingBean介面為bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是繼承該介面的類,在初始化bean的時候都會執行該方法。

AbstractRoutingDataSourceafterPropertiesSet方法的實現。

public void afterPropertiesSet() {
    if (this.targetDataSources == null) {
        throw new IllegalArgumentException("Property 'targetDataSources' is required");
    } else {
        this.resolvedDataSources = new HashMap(this.targetDataSources.size(
            
           

相關推薦

教程Spring+Mybatis環境配置資料來源

一、簡要概述 在做專案的時候遇到需要從兩個資料來源獲取資料,專案使用的Spring + Mybatis環境,看到網上有一些關於多資料來源的配置,自己也整理學習一下,然後自動切換實現從不同的資料來源獲取資料功能。 二、程式碼詳解 2.1 DataSourceConstants

Java mail 入門教程第一講 環境配置

本人一直在找相關的完整的java mail入門教程,可惜沒有較好和完整的,所以自學自編出一套完整的入門教程,我不敢說自己寫得很好,很完善,畢竟這也算是處女作。 本教程是本人學習java mail 的一些學習心得,大部分為原創,一些資料是我收集後整理出來給大家參考的。如

MVCSpring MVC常用配置

solver errors 自動裝箱 property byte mco 驗證 c基礎 form表單 一、SpringMVC基礎入門,創建一個HelloWorld程序 1.首先,導入SpringMVC需要的jar包。 2.添加Web.xml配置文件中關於Spring

odoowindows初始環境配置

1.安裝Python 下載安裝Python,本機裝的2.7.9。 開啟官網:https://www.python.org/ →Downloads根據自己的需要下載對應作業系統版本(odoo使用2.7即可) 分享網盤:連結:http://pan.baidu.com/s/1o8bm3jk 密

記錄spring中一個介面個實現類

重構遇到個小問題,記錄下: 錯誤資訊: *************************** APPLICATION FAILED TO START *************************** Description: Field xxxService in com.ali

SpringBootspring-boot-自動配置原理

思考:配置檔案到底能寫什麼?怎麼寫?自動配置原理; 1.自動配置原理 springboot自動配置主演通過@EnableConfiguration,@Conditional,@EnableConfigurationProperties或者@ConfiguraionProp

Spring Boot + MyBatis + Pagehelper 配置資料來源

package top.cmnbgy.springbootibatismulidatasource.dataSource; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionF

jupyter:anaconda環境python版本下安裝不同python版本的jupyter和spyder

1.經過嘗試,Windows下還是直接anaconda navigator下選擇不同python環境安裝比較方便。 2.jupyter預設目錄的更改 1 選擇一個用於存放config檔案的資料夾 2 在cmd中進入該資料夾的路徑 3在cmd中 輸

Spring Boot 2.x基礎教程Spring Data JPA的資料來源配置

[上一篇](http://blog.didispace.com/spring-boot-learning-21-3-7/)我們介紹了在使用JdbcTemplate來做資料訪問時候的多資料來源配置實現。接下來我們繼續學習如何在使用Spring Data JPA的時候,完成多資料來源的配置和使用。 ## 新增多

springboot使用mybatis如何配置資料來源

本篇部落格提供一個工程想連線多個數據庫進行業務查詢,在原先的單個數據庫上進行配置實現多個數據庫的使用,對於springboot以及mybatis不在此進行展開介紹,只需要把程式碼按照步驟一步步貼上進你的專案,調整一下就能實現; 簡單介紹單個數據源配置 pom檔案匯入依賴 <

Spring Boot + MyBatis + Druid環境配置資料來源

專案中採用Spring Boot + MyBatis + Druid的架構,在原資料來源的基礎上需要新增一個新的資料來源。 除錯期間,發現添加了SqlSessionFactoryBean後,原資料來源有一部分欄位無法取值,後發現是application.yml中的配置失效,

JRebel外掛安裝配置與破解啟用(方案)詳細教程

JRebel 介紹   IDEA上原生是不支援熱部署的,一般更新了 Java 檔案後要手動重啟 Tomcat 伺服器,才能生效,浪費不少生命啊。目前對於idea熱部署最好的解決方案就是安裝JRebel外掛,這樣不論是更新 class 類還是更新 Spring 配置檔案都能做

Java一次SpringMVC+ Mybatis 配置資料來源經歷

現在在維護的是學校的一款資訊服務APP的後臺,最近要開發一些新功能,其中一個就是加入學校電影院的線上購票。線上購票實際上已經有一套系統了,但是是外包給別人開發的,我們拿不到程式碼只能拿到資料庫,並且也不一定能很好的相容之前的程式碼,所以需要基於這個資料庫來進行新的開發。

springspring boot的配置文件application.yml

odi ini druid convert use pac map location leg spring: datasource:# 本地庫 url: jdbc:mysql://192.168.18.7:3306/shiptrackersmart?useUni

Spring+SpringMVC+Mybatis環境配置

gmv name adsi rtu hand odi 初始 技術 監聽 文件目錄展示 resouce目錄展示 src目錄展示 WebRoot目錄展示 jdbc.propertise driver=com.mysql.jdbc.Driver url=jdbc:m

spring cloud eureka 參數配置

lint 客戶 就會 大於 health 微服務 表示 安全 穩定 eureka.client.registry-fetch-interval-seconds 表示eureka client間隔多久去拉取服務註冊信息,默認為30秒,對於api-gateway,如果要迅速獲

01Spring Boot配置文件相關

其他 boot 默認 命令行參數 標註 yml 環境 獲取 rop 1、Spring Boot 獲取屬性的屬性源,優先級從高到低 (1)命令行參數 (2)java:comp/env裏的JNDI屬性 (3)JVM系統屬性 (4)操作系統的環境變量 (5)隨機生成的的帶rand

求購個偽基站少錢教程

聯系 事業 政府 指數 機構 民生 創新 通過 顯示 求購個偽基站多少錢【咨/詢:1587乄6350乄964 】微.信已開通網頁 打不開請直接聯系,我們有實體,本報廣州7月12日電??(記者羅艾樺)日前,廣州市公益慈善聯合會發布首份區域慈善指數報告。  報告

webpack結合React開發環境配置React開發環境配置之Webpack結合Babel8.x版本安裝的正確姿勢(Webpack最新版4.x結合Babel8.x環境配置步驟)

1. 安裝cnpmnpm install -g cnpm --registry=https://registry.npm.taobao.org【使用淘寶映象】2. 初始化package.json檔案cnpm init -y3. 安裝webpackcnpm install -d webpack webpack-

Java一次SpringMVC+ ibatis 配置資料來源經歷

    問題 如何在一個web專案中使用兩個資料來源,並且不同的介面可以按需選擇資料庫。 方案 最開始的做法 因為我們的專案用的是ibatis作為ORM框架,在其配置檔案中可以配置資料來源資訊,原始配置如下: spring-application.xm