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

Spring 實(shí)現(xiàn)數(shù)據(jù)庫(kù)讀寫分離的示例

 更新時(shí)間:2017年01月05日 14:22:50   作者:濤叔  
現(xiàn)在大型的電子商務(wù)系統(tǒng),在數(shù)據(jù)庫(kù)層面大都采用讀寫分離技術(shù),我們通常的做法就是把查詢從主庫(kù)中抽取出來(lái),采用多個(gè)從庫(kù),使用負(fù)載均衡,減輕每個(gè)從庫(kù)的查詢壓力。

現(xiàn)在大型的電子商務(wù)系統(tǒng),在數(shù)據(jù)庫(kù)層面大都采用讀寫分離技術(shù),就是一個(gè)Master數(shù)據(jù)庫(kù),多個(gè)Slave數(shù)據(jù)庫(kù)。Master庫(kù)負(fù)責(zé)數(shù)據(jù)更新和實(shí)時(shí)數(shù)據(jù)查詢,Slave庫(kù)當(dāng)然負(fù)責(zé)非實(shí)時(shí)數(shù)據(jù)查詢。因?yàn)樵趯?shí)際的應(yīng)用中,數(shù)據(jù)庫(kù)都是讀多寫少(讀取數(shù)據(jù)的頻率高,更新數(shù)據(jù)的頻率相對(duì)較少),而讀取數(shù)據(jù)通常耗時(shí)比較長(zhǎng),占用數(shù)據(jù)庫(kù)服務(wù)器的CPU較多,從而影響用戶體驗(yàn)。我們通常的做法就是把查詢從主庫(kù)中抽取出來(lái),采用多個(gè)從庫(kù),使用負(fù)載均衡,減輕每個(gè)從庫(kù)的查詢壓力。

采用讀寫分離技術(shù)的目標(biāo):有效減輕Master庫(kù)的壓力,又可以把用戶查詢數(shù)據(jù)的請(qǐng)求分發(fā)到不同的Slave庫(kù),從而保證系統(tǒng)的健壯性。我們看下采用讀寫分離的背景。

隨著網(wǎng)站的業(yè)務(wù)不斷擴(kuò)展,數(shù)據(jù)不斷增加,用戶越來(lái)越多,數(shù)據(jù)庫(kù)的壓力也就越來(lái)越大,采用傳統(tǒng)的方式,比如:數(shù)據(jù)庫(kù)或者SQL的優(yōu)化基本已達(dá)不到要求,這個(gè)時(shí)候可以采用讀寫分離的策 略來(lái)改變現(xiàn)狀。

具體到開發(fā)中,如何方便的實(shí)現(xiàn)讀寫分離呢?目前常用的有兩種方式:

1 第一種方式是我們最常用的方式,就是定義2個(gè)數(shù)據(jù)庫(kù)連接,一個(gè)是MasterDataSource,另一個(gè)是SlaveDataSource。更新數(shù)據(jù)時(shí)我們讀取MasterDataSource,查詢數(shù)據(jù)時(shí)我們讀取SlaveDataSource。這種方式很簡(jiǎn)單,我就不贅述了。

2 第二種方式動(dòng)態(tài)數(shù)據(jù)源切換,就是在程序運(yùn)行時(shí),把數(shù)據(jù)源動(dòng)態(tài)織入到程序中,從而選擇讀取主庫(kù)還是從庫(kù)。主要使用的技術(shù)是:annotation,Spring AOP ,反射。下面會(huì)詳細(xì)的介紹實(shí)現(xiàn)方式。

在介紹實(shí)現(xiàn)方式之前,我們先準(zhǔn)備一些必要的知識(shí),spring 的AbstractRoutingDataSource 類

AbstractRoutingDataSource這個(gè)類 是spring2.0以后增加的,我們先來(lái)看下AbstractRoutingDataSource的定義:

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

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}

AbstractRoutingDataSource繼承了AbstractDataSource ,而AbstractDataSource 又是DataSource 的子類。DataSource 是javax.sql 的數(shù)據(jù)源接口,定義如下:

public interface DataSource extends CommonDataSource,Wrapper {

 /**
  * <p>Attempts to establish a connection with the data source that
  * this <code>DataSource</code> object represents.
  *
  * @return a connection to the data source
  * @exception SQLException if a database access error occurs
  */
 Connection getConnection() throws SQLException;

 /**
  * <p>Attempts to establish a connection with the data source that
  * this <code>DataSource</code> object represents.
  *
  * @param username the database user on whose behalf the connection is
  * being made
  * @param password the user's password
  * @return a connection to the data source
  * @exception SQLException if a database access error occurs
  * @since 1.4
  */
 Connection getConnection(String username, String password)
  throws SQLException;

}

DataSource 接口定義了2個(gè)方法,都是獲取數(shù)據(jù)庫(kù)連接。我們?cè)诳聪翧bstractRoutingDataSource 如何實(shí)現(xiàn)了DataSource接口:

public Connection getConnection() throws SQLException {
    return determineTargetDataSource().getConnection();
  }

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

很顯然就是調(diào)用自己的determineTargetDataSource() 方法獲取到connection。determineTargetDataSource方法定義如下:

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;
  }

我們最關(guān)心的還是下面2句話:

Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);

determineCurrentLookupKey方法返回lookupKey,resolvedDataSources方法就是根據(jù)lookupKey從Map中獲得數(shù)據(jù)源。resolvedDataSources 和determineCurrentLookupKey定義如下:

  private Map<Object, DataSource> resolvedDataSources;

  protected abstract Object determineCurrentLookupKey()

看到以上定義,我們是不是有點(diǎn)思路了,resolvedDataSources是Map類型,我們可以把MasterDataSource和SlaveDataSource存到Map中,如下:

key  value
master MasterDataSource
slave SlaveDataSource

我們?cè)趯懸粋€(gè)類DynamicDataSource 繼承AbstractRoutingDataSource,實(shí)現(xiàn)其determineCurrentLookupKey() 方法,該方法返回Map的key,master或slave。

好了,說(shuō)了這么多,有點(diǎn)煩了,下面我們看下怎么實(shí)現(xiàn)。

上面已經(jīng)提到了我們要使用的技術(shù),我們先看下annotation的定義:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
  String value();
}

我們還需要實(shí)現(xiàn)spring的抽象類AbstractRoutingDataSource,就是實(shí)現(xiàn)determineCurrentLookupKey方法:

public class DynamicDataSource extends AbstractRoutingDataSource {

  @Override
  protected Object determineCurrentLookupKey() {
    // TODO Auto-generated method stub
    return DynamicDataSourceHolder.getDataSouce();
  }

}


public class DynamicDataSourceHolder {
  public static final ThreadLocal<String> holder = new ThreadLocal<String>();

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

  public static String getDataSouce() {
    return holder.get();
  }
}

從DynamicDataSource 的定義看出,他返回的是DynamicDataSourceHolder.getDataSouce()值,我們需要在程序運(yùn)行時(shí)調(diào)用DynamicDataSourceHolder.putDataSource()方法,對(duì)其賦值。下面是我們實(shí)現(xiàn)的核心部分,也就是AOP部分,DataSourceAspect定義如下:

public class DataSourceAspect {

  public void before(JoinPoint point)
  {
    Object target = point.getTarget();
    String method = point.getSignature().getName();

    Class<?>[] classz = target.getClass().getInterfaces();

    Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
        .getMethod().getParameterTypes();
    try {
      Method m = classz[0].getMethod(method, parameterTypes);
      if (m != null && m.isAnnotationPresent(DataSource.class)) {
        DataSource data = m
            .getAnnotation(DataSource.class);
        DynamicDataSourceHolder.putDataSource(data.value());
        System.out.println(data.value());
      }
      
    } catch (Exception e) {
      // TODO: handle exception
    }
  }
}

為了方便測(cè)試,我定義了2個(gè)數(shù)據(jù)庫(kù),shop模擬Master庫(kù),test模擬Slave庫(kù),shop和test的表結(jié)構(gòu)一致,但數(shù)據(jù)不同,數(shù)據(jù)庫(kù)配置如下:

<bean id="masterdataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/shop" />
    <property name="username" value="root" />
    <property name="password" value="yangyanping0615" />
  </bean>

  <bean id="slavedataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="yangyanping0615" />
  </bean>
  
    <beans:bean id="dataSource" class="com.air.shop.common.db.DynamicDataSource">
    <property name="targetDataSources"> 
       <map key-type="java.lang.String"> 
         <!-- write -->
         <entry key="master" value-ref="masterdataSource"/> 
         <!-- read -->
         <entry key="slave" value-ref="slavedataSource"/> 
       </map> 
       
    </property> 
    <property name="defaultTargetDataSource" ref="masterdataSource"/> 
  </beans:bean>

  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>


  <!-- 配置SqlSessionFactoryBean -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:config/mybatis-config.xml" />
  </bean>

在spring的配置中增加aop配置

<!-- 配置數(shù)據(jù)庫(kù)注解aop -->
  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  <beans:bean id="manyDataSourceAspect" class="com.air.shop.proxy.DataSourceAspect" />
  <aop:config>
    <aop:aspect id="c" ref="manyDataSourceAspect">
      <aop:pointcut id="tx" expression="execution(* com.air.shop.mapper.*.*(..))"/>
      <aop:before pointcut-ref="tx" method="before"/>
    </aop:aspect>
  </aop:config>
  <!-- 配置數(shù)據(jù)庫(kù)注解aop -->

下面是MyBatis的UserMapper的定義,為了方便測(cè)試,登錄讀取的是Master庫(kù),用戶列表讀取Slave庫(kù):

public interface UserMapper {
  @DataSource("master")
  public void add(User user);

  @DataSource("master")
  public void update(User user);

  @DataSource("master")
  public void delete(int id);

  @DataSource("slave")
  public User loadbyid(int id);

  @DataSource("master")
  public User loadbyname(String name);
  
  @DataSource("slave")
  public List<User> list();
} 

好了,運(yùn)行我們的Eclipse看看效果,輸入用戶名admin 登錄看看效果

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

相關(guān)文章

  • @validated注解異常返回JSON值方式

    @validated注解異常返回JSON值方式

    這篇文章主要介紹了@validated注解異常返回JSON值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java中構(gòu)造方法set/get和toString的使用詳解

    Java中構(gòu)造方法set/get和toString的使用詳解

    這篇文章主要介紹了Java中構(gòu)造方法set/get和toString的使用詳解,構(gòu)造函數(shù)的最大作用就是創(chuàng)建對(duì)象時(shí)完成初始化,當(dāng)我們?cè)趎ew一個(gè)對(duì)象并傳入?yún)?shù)的時(shí)候,會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù)并完成參數(shù)的初始化,需要的朋友可以參考下
    2019-07-07
  • 非maven項(xiàng)目快速轉(zhuǎn)換為maven項(xiàng)目的方法步驟

    非maven項(xiàng)目快速轉(zhuǎn)換為maven項(xiàng)目的方法步驟

    時(shí)候我們導(dǎo)入的項(xiàng)目并不是有maven來(lái)管理依賴的,而是要手動(dòng)添加jar包,比較麻煩,本文主要介紹了非maven項(xiàng)目快速轉(zhuǎn)換為maven項(xiàng)目的方法步驟,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • 關(guān)于Java中的IO流總結(jié)(推薦)

    關(guān)于Java中的IO流總結(jié)(推薦)

    下面小編就為大家?guī)?lái)一篇關(guān)于Java中的IO流總結(jié)(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • java比較兩個(gè)json文件的差異及說(shuō)明

    java比較兩個(gè)json文件的差異及說(shuō)明

    這篇文章主要介紹了java比較兩個(gè)json文件的差異及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Java無(wú)限級(jí)樹(遞歸)超實(shí)用案例

    Java無(wú)限級(jí)樹(遞歸)超實(shí)用案例

    下面小編就為大家?guī)?lái)一篇Java無(wú)限級(jí)樹(遞歸)超實(shí)用案例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-11-11
  • JVM中對(duì)象的創(chuàng)建與OOP-Klass模型

    JVM中對(duì)象的創(chuàng)建與OOP-Klass模型

    這篇文章主要介紹了JVM中對(duì)象的創(chuàng)建與OOP-Klass模型,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java如何判斷整數(shù)溢出,溢出后怎么得到提示

    Java如何判斷整數(shù)溢出,溢出后怎么得到提示

    這篇文章主要介紹了Java如何判斷整數(shù)溢出,溢出后怎么得到提示,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • SpringBoot使用mybatis-plus分頁(yè)查詢無(wú)效的問(wèn)題解決

    SpringBoot使用mybatis-plus分頁(yè)查詢無(wú)效的問(wèn)題解決

    MyBatis-Plus提供了很多便捷的功能,包括分頁(yè)查詢,本文主要介紹了SpringBoot使用mybatis-plus分頁(yè)查詢無(wú)效的問(wèn)題解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • Java如何判斷一個(gè)url是否有效

    Java如何判斷一個(gè)url是否有效

    URL類提供了一個(gè)openConnection()方法,可以打開一個(gè)連接并返回一個(gè)URLConnection對(duì)象,該對(duì)象可以用來(lái)檢查連接是否有效,這篇文章主要介紹了Java判斷一個(gè)url是否有效,需要的朋友可以參考下
    2023-09-09

最新評(píng)論

墨脱县| 新竹县| 金寨县| 西青区| 清新县| 山东省| 定兴县| 大渡口区| 大埔县| 彝良县| 隆化县| 郯城县| 海林市| 荣成市| 韩城市| 突泉县| 乌海市| 铜川市| 浪卡子县| 板桥市| 黄龙县| 朝阳区| 宁海县| 连南| 泽州县| 霍州市| 托里县| 桦川县| 大宁县| 石棉县| 望江县| 沙湾县| 富锦市| 盐边县| 黄骅市| 昭觉县| 普洱| 沙洋县| 探索| 登封市| 涞源县|