SSM-SSM框架下mysql主从分离(动态加载数据源)代码实现及配置

服务器部署MySQL主从请看此博客

Docker-一台服务器用docker实现mysql主从配置

在dao包下编写的拦截器代码。

  • DynamicDataSourceHolder:
package com.dao.split;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Created with IntelliJ IDEA
 * @Description: 动态的选择需要的数据源
 * @Package: com.dao.split
 * @author: FLy-Fly-Zhang
 * @Date: 2020/2/10
 * @Time: 12:22
 */
public class DynamicDataSourceHolder {
    private static Logger logger=LoggerFactory.getLogger(DynamicDataSourceHolder.class);
    //线程本地变量
    //ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量
    private static ThreadLocal<String> contextHolder=new ThreadLocal<>();
    public static final String DB_MASTER="master";
    //若是多个从库,这里可以变为一个常量数组,在返回slave的地方使用random随机取
    public static final String DB_SLAVE="slave";
    /**
     * 获取线程的DBType
     * @return
     */
    public static String getDbType(){
        String db=contextHolder.get();
        if(db==null){
            db=DB_MASTER;
        }
        return db;
    }

    /**
     * 设置线程的dbType
     * @param db
     */
    public static void setDbtype(String db){
        logger.debug("所使用的数据源为"+db);
        contextHolder.set(db);
    }
    /**
     * 清理连接类型
     */
    public static void clearDBType(){
        contextHolder.remove();
    }
}

  • DynamicDataSourceInterceptor
package com.dao.split;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Locale;
import java.util.Properties;


/**
 * @Created with IntelliJ IDEA
 * @Description: mybatis拦截器
 * 用来拦截sql信息,并通过判断返回使用主从库
 * @Package: com.dao.split
 * @author: FLy-Fly-Zhang
 * @Date: 2020/2/10
 * @Time: 12:49
 */
//mybatis会将增删改的操作封装在update里面,select封装在query里面
// 需要拦截的方法所在的类,方法名,参数,返回值
@Intercepts({@Signature(type = Executor.class,method = "update",args={MappedStatement.class,Object.class}),
        @Signature(type = Executor.class,method = "query",
                args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})
public class DynamicDataSourceInterceptor implements Interceptor {
    private Logger logger=LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);
    //\u0020 空格 判断字符串是不是这三个字符打头
    private static final String REGEX=".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //判断当前操作是不是事务的
        boolean synchronizationActive=TransactionSynchronizationManager.isActualTransactionActive();
        String lookupKey=DynamicDataSourceHolder.DB_MASTER;;
        Object[] objects=invocation.getArgs(); //获取sql参数
        MappedStatement mappedStatement=(MappedStatement)objects[0]; //objectsz中第一个参数就用来确定是CRUD
        //不是事务
        if(!synchronizationActive){
            //读方法
            if(mappedStatement.getSqlCommandType().equals(SqlCommandType.SELECT)){
                //selectKey 为自增id查询主键即(select LAST_INSERT_ID()) 方法,使用主库查询
                //一般我们插入数据返回自增主键时就调用select LAST_INSERT_ID()方法,插入时为主库,
                // 查询时若切换则会导致数据发生错误
                if(mappedStatement.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)){
                    //lookupKey=DynamicDataSourceHolder.DB_MASTER;
                }else{
                    //绑定sql  objects[1]第二个参数就是sql
                    BoundSql boundSql=mappedStatement.getSqlSource().getBoundSql(objects[1]);
                    String sql=boundSql.getSql().toLowerCase(Locale.CHINA) //sql转换为中国时区
                            //将所有的制表符,换行符,回车替换为空格==>将sql变为一行
                            .replaceAll("\\t\\n\\r"," ");
                    //正则表达式匹配
                    if(sql.matches(REGEX)){
                        //lookupKey=DynamicDataSourceHolder.DB_MASTER;
                    }else{
                        lookupKey=DynamicDataSourceHolder.DB_SLAVE;
                    }
                }
            }
        }
        //else{ //事务类,都会改数据
            //lookupKey=DynamicDataSourceHolder.DB_MASTER;
        //}
        //[{}] 占位符,用来替换后面的参数
        logger.debug("设置方法[{}] use[{}]Strategy,SqlCommonType[{}]..",
                mappedStatement.getId(),
                lookupKey,
                mappedStatement.getSqlCommandType().name());
        DynamicDataSourceHolder.setDbtype(lookupKey);
        return invocation.proceed(); //结束拦截,程序继续进行
    }

    /**
     * 拦截配置
     * @param o
     * @return
     */
    @Override
    public Object plugin(Object o) {
        //如果拦截的对象是Executor类型的,就进行拦截
        //Executor 在mybatis中做CRUD操作
        if(o instanceof Executor)
            return Plugin.wrap(o,this);

        return o;
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

  • DynamicDataSource
package com.dao.split;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @Created with IntelliJ IDEA
 * @Description: 动态数据源:返回拦截后输出的结果
 * @Package: com.dao.split
 * @author: FLy-Fly-Zhang
 * @Date: 2020/2/10
 * @Time: 12:14
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDbType();
    }
}

配置文件:

这是我自己的配置文件布局。
SSM-SSM框架下mysql主从分离(动态加载数据源)代码实现及配置_第1张图片

首先在mybatis-config.xml 文件中将自定义的拦截器配置进去。


<configuration>
    
    <settings>
        
        <setting name="useGeneratedKeys" value="true"/>
        
        <setting name="useColumnLabel" value="true"/>
        
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    settings>
    <plugins>
        
        <plugin interceptor="com.dao.split.DynamicDataSourceInterceptor">plugin>
    plugins>
configuration>
其次在spring-dao文件中,配置主从数据源

<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"
       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">
        
    
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    
    <bean id="abstractDataSource" abstract="true" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        
        
        
        
        
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        
        <property name="autoCommitOnClose" value="false"/>
        
        <property name="checkoutTimeout" value="10000"/>
        
        <property name="acquireRetryAttempts" value="2"/>
    bean>
    
    <bean id="master" parent="abstractDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.master.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    bean>
    
    <bean id="slave" parent="abstractDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.slave.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    bean>
    
    <bean id="dynamicDataSource" class="com.dao.split.DynamicDataSource">
        <property name="targetDataSources">
            <map>
                
                <entry value-ref="master" key="master">entry>
                
                <entry value-ref="slave" key="slave">entry>
            map>
        property>
    bean>
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource">
            <ref bean="dynamicDataSource"/>
        property>
    bean>
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="typeAliasesPackage" value="com.entity"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    bean>
    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        
        <property name="basePackage" value="com.dao"/>

    bean>
beans>

注意:

需要注意的是,我在拦截器代码中加了日志,若您也想配置日志,请参考此篇博客:Log-ssm中日志文件的配置

若不想配置,只需要删除出现Logger的那两行代码。

在此,我也将我的pom文件放在此处,仅供参考。



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0modelVersion>

  <groupId>comgroupId>
  <artifactId>storeartifactId>
  <version>1.0.0-SNAPSHOTversion>
  <packaging>warpackaging>

  <name>store Maven Webappname>
  
  <url>http://maven.apache.orgurl>

  <properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <maven.compiler.source>1.7maven.compiler.source>
    <maven.compiler.target>1.7maven.compiler.target>
    
    <spring.version>4.3.7.RELEASEspring.version>
  properties>

  <dependencies>
    <dependency>
      <groupId>junitgroupId>
      <artifactId>junitartifactId>
      <version>4.12version>
      <scope>testscope>
    dependency>
    
    <dependency>
      <groupId>ch.qos.logbackgroupId>
      <artifactId>logback-classicartifactId>
      <version>1.2.3version>
    dependency>

    
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-coreartifactId>
      <version>${spring.version}version>
    dependency>
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-beansartifactId>
      <version>${spring.version}version>
    dependency>
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-contextartifactId>
      <version>${spring.version}version>
    dependency>
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-jdbcartifactId>
      <version>${spring.version}version>
    dependency>
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-txartifactId>
      <version>${spring.version}version>
    dependency>
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-webartifactId>
      <version>${spring.version}version>
    dependency>
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-webmvcartifactId>
      <version>${spring.version}version>
    dependency>
    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-testartifactId>
      <version>${spring.version}version>
      <scope>testscope>
    dependency>

    
    <dependency>
      <groupId>javax.servletgroupId>
      <artifactId>javax.servlet-apiartifactId>
      <version>3.1.0version>
    dependency>
    
    <dependency>
      <groupId>com.fasterxml.jackson.coregroupId>
      <artifactId>jackson-databindartifactId>
      <version>2.8.7version>
    dependency>
    
    <dependency>
      <groupId>commons-collectionsgroupId>
      <artifactId>commons-collectionsartifactId>
      <version>3.2.2version>
    dependency>
    
    <dependency>
      <groupId>commons-fileuploadgroupId>
      <artifactId>commons-fileuploadartifactId>
      <version>1.3.2version>
    dependency>
    
    <dependency>
      <groupId>org.mybatisgroupId>
      <artifactId>mybatisartifactId>
      <version>3.4.2version>
    dependency>
    
    <dependency>
      <groupId>org.mybatisgroupId>
      <artifactId>mybatis-springartifactId>
      <version>1.3.0version>
    dependency>
      <dependency>
          <groupId>com.mchangegroupId>
          <artifactId>c3p0artifactId>
          <version>0.9.5.4version>
      dependency>
    <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
      <version>5.1.37version>
    dependency>
    
    <dependency>
      <groupId>net.coobirdgroupId>
      <artifactId>thumbnailatorartifactId>
      <version>0.4.8version>
    dependency>
    
    <dependency>
      <groupId>com.github.pengglegroupId>
      <artifactId>kaptchaartifactId>
      <version>2.3.2version>
    dependency>
  dependencies>

  <build>
    <finalName>storefinalName>
    <plugins>
      <plugin>
        
        <groupId>org.apache.maven.pluginsgroupId>
        <artifactId>maven-compiler-pluginartifactId>
        <version>3.6.1version>
        <configuration>
          <source>1.8source>
          <target>1.8target>
          <encoding>UTF8encoding>
        configuration>
      plugin>
    plugins>
   
  build>
project>

你可能感兴趣的:(SSM)