>  기사  >  데이터 베이스  >  Spring이 mybatis를 통합하여 mysql 데이터베이스의 읽기 및 쓰기 분리를 실현하는 방법에 대한 예제 분석

Spring이 mybatis를 통합하여 mysql 데이터베이스의 읽기 및 쓰기 분리를 실현하는 방법에 대한 예제 분석

黄舟
黄舟원래의
2017-08-09 14:17:261681검색

이 기사에서는 mybatis를 mybatis와 Spring이 통합하여 mysql 데이터베이스의 읽기와 쓰기 분리를 구현하는 방법을 예제 코드를 통해 소개합니다. 필요한 친구는 이를 참고할 수 있습니다. 일정 규모 이상에서는 과도한 부하 압력으로 인해 데이터베이스 장애가 발생하여 웹 사이트에 병목 현상이 발생합니다. 다행스럽게도 현재 주류 데이터베이스의 대부분은 마스터-슬레이브 상시 대기 기능을 제공합니다. 두 데이터베이스 간의 마스터-슬레이브 관계를 구성하면 한 데이터베이스의 데이터 업데이트를 다른 서버에 동기화할 수 있습니다. 웹사이트는 데이터베이스의 이 기능을 사용하여 데이터베이스 읽기와 쓰기의 분리를 실현함으로써 데이터베이스 부하 압력을 개선합니다. 아래 그림과 같이

애플리케이션 서버가 데이터를 쓸 때 마스터 데이터베이스에 액세스합니다. 이러한 방식으로 마스터-슬레이브 복제 메커니즘을 통해 데이터 업데이트를 슬레이브 데이터베이스에 동기화합니다. 응용 프로그램 서버가 데이터를 읽으면 데이터베이스가 데이터를 가져옵니다. 애플리케이션 프로그램이 읽기-쓰기 분리된 데이터베이스에 액세스할 수 있도록 하기 위해 일반적으로 애플리케이션 서버에서 특수 데이터베이스 액세스 모듈을 사용하여 데이터베이스 읽기-쓰기 분리를 애플리케이션에 투명하게 만듭니다.

                                                                                            ,,,,,,,,,,,,,,,,,, 사용할 수 있습니다 데이터베이스에 액세스합니다. 또한, mysql 데이터베이스의 마스터-슬레이브 복제에 대해서는 mysql5.7.18의 내 설치 및 마스터-슬레이브 복제를 참고할 수 있습니다. 데이터베이스를 읽기 및 쓰기에서 분리하려면 먼저 데이터베이스에서 마스터-슬레이브 복제를 구현해야 합니다. 따라서 데이터베이스의 마스터-슬레이브 복제를 구현하지 않은 경우 먼저 데이터베이스의 마스터-슬레이브 복제를 구현해야 합니다

읽기-쓰기 데이터 소스 구성(마스터-슬레이브 데이터베이스)

Mybatis-spring Mybatis-spring .xml

#主数据库数据源
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.0.4:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=1
jdbc.minIdle=1
jdbc.maxActive=20
jdbc.maxWait=60000
jdbc.removeAbandoned=true
jdbc.removeAbandonedTimeout=180
jdbc.timeBetweenEvictionRunsMillis=60000
jdbc.minEvictableIdleTimeMillis=300000
jdbc.validationQuery=SELECT 1
jdbc.testWhileIdle=true
jdbc.testOnBorrow=false
jdbc.testOnReturn=false
#从数据库数据源
slave.jdbc.driverClassName=com.mysql.jdbc.Driver
slave.jdbc.url=jdbc:mysql://192.168.0.221:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false
slave.jdbc.username=root
slave.jdbc.password=123456
slave.jdbc.initialSize=1
slave.jdbc.minIdle=1
slave.jdbc.maxActive=20
slave.jdbc.maxWait=60000
slave.jdbc.removeAbandoned=true
slave.jdbc.removeAbandonedTimeout=180
slave.jdbc.timeBetweenEvictionRunsMillis=60000
slave.jdbc.minEvictableIdleTimeMillis=300000
slave.jdbc.validationQuery=SELECT 1
slave.jdbc.testWhileIdle=true
slave.jdbc.testOnBorrow=false
slave.jdbc.testOnReturn=false

AOP는 데이터 소스의 동적 전환DataSource.java

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  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/context
   http://www.springframework.org/schema/context/spring-context.xsd
   http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx.xsd">
  <!-- master数据源 -->
  <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!-- 基本属性 url、user、password --> 
    <property name="driverClassName" value="${jdbc.driverClassName}" /> 
    <property name="url" value="${jdbc.url}" /> 
    <property name="username" value="${jdbc.username}" /> 
    <property name="password" value="${jdbc.password}" /> 
    <property name="initialSize" value="${jdbc.initialSize}" /> 
    <property name="minIdle" value="${jdbc.minIdle}" />  
    <property name="maxActive" value="${jdbc.maxActive}" /> 
    <property name="maxWait" value="${jdbc.maxWait}" />
    <!-- 超过时间限制是否回收 -->
    <property name="removeAbandoned" value="${jdbc.removeAbandoned}" />
    <!-- 超过时间限制多长; -->
    <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" />
    <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
    <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
    <!-- 用来检测连接是否有效的sql,要求是一个查询语句-->
    <property name="validationQuery" value="${jdbc.validationQuery}" />
    <!-- 申请连接的时候检测 -->
    <property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
    <!-- 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 -->
    <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
    <!-- 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能 -->
    <property name="testOnReturn" value="${jdbc.testOnReturn}" />
  </bean>
  <!-- slave数据源 -->
  <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${slave.jdbc.driverClassName}" /> 
    <property name="url" value="${slave.jdbc.url}" /> 
    <property name="username" value="${slave.jdbc.username}" /> 
    <property name="password" value="${slave.jdbc.password}" /> 
    <property name="initialSize" value="${slave.jdbc.initialSize}" /> 
    <property name="minIdle" value="${slave.jdbc.minIdle}" />  
    <property name="maxActive" value="${slave.jdbc.maxActive}" /> 
    <property name="maxWait" value="${slave.jdbc.maxWait}" />
    <property name="removeAbandoned" value="${slave.jdbc.removeAbandoned}" />
    <property name="removeAbandonedTimeout" value="${slave.jdbc.removeAbandonedTimeout}" />
    <property name="timeBetweenEvictionRunsMillis" value="${slave.jdbc.timeBetweenEvictionRunsMillis}" />
    <property name="minEvictableIdleTimeMillis" value="${slave.jdbc.minEvictableIdleTimeMillis}" />
    <property name="validationQuery" value="${slave.jdbc.validationQuery}" />
    <property name="testWhileIdle" value="${slave.jdbc.testWhileIdle}" />
    <property name="testOnBorrow" value="${slave.jdbc.testOnBorrow}" />
    <property name="testOnReturn" value="${slave.jdbc.testOnReturn}" />
  </bean>
  <!-- 动态数据源,根据service接口上的注解来决定取哪个数据源 -->
  <bean id="dataSource" class="com.yzb.util.DynamicDataSource"> 
    <property name="targetDataSources">   
     <map key-type="java.lang.String">   
       <!-- write or slave -->  
       <entry key="slave" value-ref="slaveDataSource"/>   
       <!-- read or master  --> 
       <entry key="master" value-ref="masterDataSource"/>   
     </map>        
    </property>  
    <property name="defaultTargetDataSource" ref="masterDataSource"/>   
  </bean>
  <!-- Mybatis文件 -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="configLocation" value="classpath:mybatis-config.xml" /> 
    <property name="dataSource" ref="dataSource" />
    <!-- 映射文件路径 -->
    <property name="mapperLocations" value="classpath*:dbmappers/*.xml" />
  </bean>
  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.yzb.dao" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
  </bean>
  <!-- 事务管理器 -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <!-- 声明式开启 -->
  <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="1"/>
  <!-- 为业务逻辑层的方法解析@DataSource注解 为当前线程的HandleDataSource注入数据源 -->  
  <bean id="dataSourceAspect" class="com.yzb.util.DataSourceAspect" />  
  <aop:config proxy-target-class="true">  
    <aop:aspect id="dataSourceAspect" ref="dataSourceAspect" order="2">  
      <aop:pointcut id="tx" expression="execution(* com.yzb.service.impl..*.*(..)) "/>  
      <aop:before pointcut-ref="tx" method="before" />        
    </aop:aspect>  
  </aop:config>
</beans>
DataSourceAspect을 구현합니다. java


package com.yzb.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** 
 * RUNTIME 
 * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。 
 * 
 */ 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface DataSource
{
  String value();
}

DynamicDataSource.java

package com.yzb.util;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
public class DataSourceAspect
{
  /**
   * 在dao层方法获取datasource对象之前,在切面中指定当前线程数据源
   */
  public void before(JoinPoint point)
  {
    Object target = point.getTarget();
    String method = point.getSignature().getName();
    Class<?>[] classz = target.getClass().getInterfaces();            // 获取目标类的接口, 所以@DataSource需要写在接口上
    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);
        System.out.println("用户选择数据库库类型:" + data.value());
        HandleDataSource.putDataSource(data.value());            // 数据源放到当前线程中
      }
    } catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  }

HandleDataSource.java

package com.yzb.util;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource
{
  /**
   * 获取与数据源相关的key 此key是Map<String,DataSource> resolvedDataSources 中与数据源绑定的key值
   * 在通过determineTargetDataSource获取目标数据源时使用
   */
  @Override
  protected Object determineCurrentLookupKey()
  {
    return HandleDataSource.getDataSource();
  }
}

서비스 인터페이스에서 @DataSource를 사용하여 데이터 소스를 지정하세요


package com.yzb.util;
public class HandleDataSource
{
  public static final ThreadLocal<String> holder = new ThreadLocal<String>();
  /**
   * 绑定当前线程数据源
   * 
   * @param key
   */
  public static void putDataSource(String datasource)
  {
    holder.set(datasource);
  }
  /**
   * 获取当前线程的数据源
   * 
   * @return
   */
  public static String getDataSource()
  {
    return holder.get();
  }
}

주의사항

테스트할 때 데이터베이스에서 읽고 있다는 것을 어떻게 알 수 있나요? 마스터 데이터베이스와 슬레이브 데이터베이스를 구별하기 위해 데이터베이스에서 쿼리된 레코드의 특정 필드 값을 수정할 수 있습니다. ~                         " " - 레코드 내 수정 가능 Aops가 여러 개인 경우 순서에 주의해야 합니다. aop 위빙, 위빙 순서를 제어하려면 order 키워드를 사용하세요


Summary


위 내용은 Spring이 mybatis를 통합하여 mysql 데이터베이스의 읽기 및 쓰기 분리를 실현하는 방법에 대한 예제 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:MySQL 공통 구문다음 기사:MySQL 공통 구문