欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring(AbstractRoutingDataSource)實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換示例

 更新時(shí)間:2017年02月21日 14:45:25   作者:一天不進(jìn)步,就是退步  
本篇文章主要介紹了詳解Spring(AbstractRoutingDataSource)實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。

一、前言

近期一項(xiàng)目A需實(shí)現(xiàn)數(shù)據(jù)同步到另一項(xiàng)目B數(shù)據(jù)庫(kù)中,在不改變B項(xiàng)目的情況下,只好選擇項(xiàng)目A中切換數(shù)據(jù)源,直接把數(shù)據(jù)寫(xiě)入項(xiàng)目B的數(shù)據(jù)庫(kù)中。這種需求,在數(shù)據(jù)同步與定時(shí)任務(wù)中經(jīng)常需要。

那么問(wèn)題來(lái)了,該如何解決多數(shù)據(jù)源問(wèn)題呢?不光是要配置多個(gè)數(shù)據(jù)源,還得能靈活動(dòng)態(tài)的切換數(shù)據(jù)源。以spring+hibernate框架項(xiàng)目為例:

單個(gè)數(shù)據(jù)源綁定給sessionFactory,再在Dao層操作,若多個(gè)數(shù)據(jù)源的話,那不是就成了下圖:

   

可見(jiàn),sessionFactory都寫(xiě)死在了Dao層,若我再添加個(gè)數(shù)據(jù)源的話,則又得添加一個(gè)sessionFactory。所以比較好的做法應(yīng)該是下圖:

接下來(lái)就為大家講解下如何用spring來(lái)整合這些數(shù)據(jù)源,同樣以spring+hibernate配置為例。

二、實(shí)現(xiàn)原理

1、擴(kuò)展Spring的AbstractRoutingDataSource抽象類(該類充當(dāng)了DataSource的路由中介, 能有在運(yùn)行時(shí), 根據(jù)某種key值來(lái)動(dòng)態(tài)切換到真正的DataSource上。)

從AbstractRoutingDataSource的源碼中:

復(fù)制代碼 代碼如下:

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean

我們可以看到,它繼承了AbstractDataSource,而AbstractDataSource不就是javax.sql.DataSource的子類,So我們可以分析下它的getConnection方法:

public Connection getConnection() throws SQLException { 
 return determineTargetDataSource().getConnection(); 
} 
 
public Connection getConnection(String username, String password) throws SQLException { 
  return determineTargetDataSource().getConnection(username, password); 
}

獲取連接的方法中,重點(diǎn)是determineTargetDataSource()方法,看源碼:

/** 
  * Retrieve the current target DataSource. Determines the 
  * {@link #determineCurrentLookupKey() current lookup key}, performs 
  * a lookup in the {@link #setTargetDataSources targetDataSources} map, 
  * falls back to the specified 
  * {@link #setDefaultTargetDataSource default target DataSource} if necessary. 
  * @see #determineCurrentLookupKey() 
  */ 
 protected DataSource determineTargetDataSource() { 
  Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); 
  Object lookupKey = determineCurrentLookupKey(); 
  DataSource dataSource = this.resolvedDataSources.get(lookupKey); 
  if (dataSource == null && (this.lenientFallback || lookupKey == null)) { 
   dataSource = this.resolvedDefaultDataSource; 
  } 
  if (dataSource == null) { 
   throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); 
  } 
  return dataSource; 
 }

上面這段源碼的重點(diǎn)在于determineCurrentLookupKey()方法,這是AbstractRoutingDataSource類中的一個(gè)抽象方法,而它的返回值是你所要用的數(shù)據(jù)源dataSource的key值,有了這個(gè)key值,resolvedDataSource(這是個(gè)map,由配置文件中設(shè)置好后存入的)就從中取出對(duì)應(yīng)的DataSource,如果找不到,就用配置默認(rèn)的數(shù)據(jù)源。

看完源碼,應(yīng)該有點(diǎn)啟發(fā)了吧,沒(méi)錯(cuò)!你要擴(kuò)展AbstractRoutingDataSource類,并重寫(xiě)其中的determineCurrentLookupKey()方法,來(lái)實(shí)現(xiàn)數(shù)據(jù)源的切換:

package com.datasource.test.util.database;
 
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
/**
 * 獲取數(shù)據(jù)源(依賴于spring)
 * @author linhy
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
 @Override
 protected Object determineCurrentLookupKey() {
  return DataSourceHolder.getDataSource();
 }
}

DataSourceHolder這個(gè)類則是我們自己封裝的對(duì)數(shù)據(jù)源進(jìn)行操作的類:

package com.datasource.test.util.database;
 
/**
 * 數(shù)據(jù)源操作
 * @author linhy
 */
public class DataSourceHolder {
 //線程本地環(huán)境
 private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
 //設(shè)置數(shù)據(jù)源
 public static void setDataSource(String customerType) {
  dataSources.set(customerType);
 }
 //獲取數(shù)據(jù)源
 public static String getDataSource() {
  return (String) dataSources.get();
 }
 //清除數(shù)據(jù)源
 public static void clearDataSource() {
  dataSources.remove();
 }
 
}

2、有人就要問(wèn),那你setDataSource這方法是要在什么時(shí)候執(zhí)行呢?當(dāng)然是在你需要切換數(shù)據(jù)源的時(shí)候執(zhí)行啦。手動(dòng)在代碼中調(diào)用寫(xiě)死嗎?這是多蠢的方法,當(dāng)然要讓它動(dòng)態(tài)咯。所以我們可以應(yīng)用spring aop來(lái)設(shè)置,把配置的數(shù)據(jù)源類型都設(shè)置成為注解標(biāo)簽,在service層中需要切換數(shù)據(jù)源的方法上,寫(xiě)上注解標(biāo)簽,調(diào)用相應(yīng)方法切換數(shù)據(jù)源咯(就跟你設(shè)置事務(wù)一樣):

@DataSource(name=DataSource.slave1)
public List getProducts(){

當(dāng)然,注解標(biāo)簽的用法可能很少人用到,但它可是個(gè)好東西哦,大大的幫助了我們開(kāi)發(fā):

package com.datasource.test.util.database;
 
import java.lang.annotation.*;
 
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
 String name() default DataSource.master;
 
 public static String master = "dataSource1";
 
 public static String slave1 = "dataSource2";
 
 public static String slave2 = "dataSource3";
 
}

三、配置文件

為了精簡(jiǎn)篇幅,省略了無(wú)關(guān)本內(nèi)容主題的配置。

項(xiàng)目中單獨(dú)分離出application-database.xml,關(guān)于數(shù)據(jù)源配置的文件。

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 數(shù)據(jù)庫(kù)相關(guān)配置 放在這里 -->
<beans xmlns="http://www.springframework.org/schema/beans"
  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"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
 
 <bean id = "dataSource1" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource"> 
  <property name="url" value="${db1.url}"/>
  <property name = "user" value = "${db1.user}"/>
  <property name = "password" value = "${db1.pwd}"/>
  <property name="autoReconnect" value="true"/>
  <property name="useUnicode" value="true"/>
  <property name="characterEncoding" value="UTF-8"/>
 </bean>
 
 <bean id = "dataSource2" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
  <property name="url" value="${db2.url}"/>
  <property name = "user" value = "${db2.user}"/>
  <property name = "password" value = "${db2.pwd}"/>
  <property name="autoReconnect" value="true"/>
  <property name="useUnicode" value="true"/>
  <property name="characterEncoding" value="UTF-8"/>
 </bean>
 
 <bean id = "dataSource3" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
  <property name="url" value="${db3.url}"/>
  <property name = "user" value = "${db3.user}"/>
  <property name = "password" value = "${db3.pwd}"/>
  <property name="autoReconnect" value="true"/>
  <property name="useUnicode" value="true"/>
  <property name="characterEncoding" value="UTF-8"/>
 </bean>
 <!-- 配置多數(shù)據(jù)源映射關(guān)系 -->
 <bean id="dataSource" class="com.datasource.test.util.database.DynamicDataSource">
  <property name="targetDataSources">
   <map key-type="java.lang.String">
  <entry key="dataSource1" value-ref="dataSource1"></entry>
    <entry key="dataSource2" value-ref="dataSource2"></entry>
    <entry key="dataSource3" value-ref="dataSource3"></entry>
   </map>
  </property>
 <!-- 默認(rèn)目標(biāo)數(shù)據(jù)源為你主庫(kù)數(shù)據(jù)源 -->
  <property name="defaultTargetDataSource" ref="dataSource1"/>
 </bean>
 
 <bean id="sessionFactoryHibernate" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">com.datasource.test.util.database.ExtendedMySQLDialect</prop>
    <prop key="hibernate.show_sql">${SHOWSQL}</prop>
    <prop key="hibernate.format_sql">${SHOWSQL}</prop>
    <prop key="query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
    <prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
    <prop key="hibernate.c3p0.max_size">30</prop>
    <prop key="hibernate.c3p0.min_size">5</prop>
    <prop key="hibernate.c3p0.timeout">120</prop>
    <prop key="hibernate.c3p0.idle_test_period">120</prop>
    <prop key="hibernate.c3p0.acquire_increment">2</prop>
    <prop key="hibernate.c3p0.validate">true</prop>
    <prop key="hibernate.c3p0.max_statements">100</prop>
   </props>
  </property>
 </bean>
 
 <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
  <property name="sessionFactory" ref="sessionFactoryHibernate"/>
 </bean>
 
 <bean id="dataSourceExchange" class="com.datasource.test.util.database.DataSourceExchange"/>
 
 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactoryHibernate"/>
 </bean>
 
 <tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
   <tx:method name="insert*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="add*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="update*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="modify*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="edit*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="del*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="save*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="send*" propagation="NESTED" rollback-for="Exception"/>
   <tx:method name="get*" read-only="true"/>
   <tx:method name="find*" read-only="true"/>
   <tx:method name="query*" read-only="true"/>
   <tx:method name="search*" read-only="true"/>
   <tx:method name="select*" read-only="true"/>
   <tx:method name="count*" read-only="true"/>
  </tx:attributes>
 </tx:advice>
 
 <aop:config>
  <aop:pointcut id="service" expression="execution(* com.datasource..*.service.*.*(..))"/>
  <!-- 關(guān)鍵配置,切換數(shù)據(jù)源一定要比持久層代碼更先執(zhí)行(事務(wù)也算持久層代碼) -->
  <aop:advisor advice-ref="txAdvice" pointcut-ref="service" order="2"/>
  <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="service" order="1"/>
 </aop:config>
 
</beans>

四、疑問(wèn)

多數(shù)據(jù)源切換是成功了,但牽涉到事務(wù)呢?單數(shù)據(jù)源事務(wù)是ok的,但如果多數(shù)據(jù)源需要同時(shí)使用一個(gè)事務(wù)呢?這個(gè)問(wèn)題有點(diǎn)頭大,網(wǎng)絡(luò)上有人提出用atomikos開(kāi)源項(xiàng)目實(shí)現(xiàn)JTA分布式事務(wù)處理。你怎么看?

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

相關(guān)文章

  • 詳解maven中央倉(cāng)庫(kù)連不上的解決辦法

    詳解maven中央倉(cāng)庫(kù)連不上的解決辦法

    這篇文章主要介紹了詳解maven中央倉(cāng)庫(kù)連不上的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之二手書(shū)商城系統(tǒng)的實(shí)現(xiàn)

    Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之二手書(shū)商城系統(tǒng)的實(shí)現(xiàn)

    這是一個(gè)使用了java+JSP+Springboot+maven+mysql+ThymeLeaf+FTP開(kāi)發(fā)的二手書(shū)商城系統(tǒng),是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有在線書(shū)城該有的所有功能,感興趣的朋友快來(lái)看看吧
    2022-01-01
  • spring jdbctemplate的用法小結(jié)

    spring jdbctemplate的用法小結(jié)

    jdbcTemplate是spring框架中提供的一個(gè)對(duì)象,是對(duì)原始繁雜的jdbc 對(duì)象的簡(jiǎn)單封裝,本文通過(guò)實(shí)例代碼介紹spring jdbctemplate的用法小結(jié),需要的朋友可以參考下
    2023-04-04
  • java字符轉(zhuǎn)碼的三種方法總結(jié)及實(shí)例

    java字符轉(zhuǎn)碼的三種方法總結(jié)及實(shí)例

    這篇文章主要介紹了 java字符轉(zhuǎn)碼的三種方法總結(jié)及實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Java Web項(xiàng)目中實(shí)現(xiàn)文件下載功能的實(shí)例教程

    Java Web項(xiàng)目中實(shí)現(xiàn)文件下載功能的實(shí)例教程

    這篇文章主要介紹了Java Web項(xiàng)目中實(shí)現(xiàn)文件下載功能的實(shí)例教程,分別講解了通過(guò)超鏈接實(shí)現(xiàn)下載以及通過(guò)Servlet程序?qū)崿F(xiàn)下載的方式,需要的朋友可以參考下
    2016-05-05
  • java中快速創(chuàng)建帶初始值的List和Map實(shí)例

    java中快速創(chuàng)建帶初始值的List和Map實(shí)例

    下面小編就為大家?guī)?lái)一篇java中快速創(chuàng)建帶初始值的List和Map實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項(xiàng)目踩過(guò)的坑

    Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項(xiàng)目踩過(guò)的坑

    IDEA旗艦版可以直接創(chuàng)建Spring MVC項(xiàng)目,但創(chuàng)建后的項(xiàng)目并不是直接就可以運(yùn)行,還需要進(jìn)行一些配置。這篇文章主要介紹了Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項(xiàng)目踩坑記 ,需要的朋友可以參考下
    2020-03-03
  • java進(jìn)階之了解SpringBoot的配置原理

    java進(jìn)階之了解SpringBoot的配置原理

    今天帶大家了解SpringBoot的相關(guān)知識(shí),文中對(duì)SpringBoot的配置原理作了非常詳細(xì)的圖文示例及介紹,需要的朋友可以參考下
    2021-06-06
  • Spring boot動(dòng)態(tài)修改日志級(jí)別的方法

    Spring boot動(dòng)態(tài)修改日志級(jí)別的方法

    我們經(jīng)常會(huì)遇到業(yè)務(wù)想看debug日志的問(wèn)題,但是debug日志頻繁打印會(huì)對(duì)日志查看有影響,且日志多對(duì)系統(tǒng)也會(huì)有一定的壓力,因此,如果可以在需要的時(shí)候動(dòng)態(tài)臨時(shí)調(diào)整下日志的級(jí)別則是比較完美的,spring boot已經(jīng)支持這種功能,需要的朋友可以參考下
    2022-12-12
  • spring?cloud?gateway中netty線程池小優(yōu)化

    spring?cloud?gateway中netty線程池小優(yōu)化

    這篇文章主要介紹了spring?cloud?gateway中netty線程池小優(yōu)化技巧示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07

最新評(píng)論