前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring+spring mvc+mybatis 实现主从数据库配置

spring+spring mvc+mybatis 实现主从数据库配置

作者头像
用户1518699
发布2018-09-12 15:31:23
8070
发布2018-09-12 15:31:23
举报
文章被收录于专栏:nice_每一天nice_每一天

一、配置文件

1、jdbc.properties
代码语言:javascript
复制
master_driverUrl=jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
master_username=root
master_password=12345

slave_driverUrl=jdbc:mysql://localhost:3306/wechat?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
slave_username=root
slave_password=12345
2、spring-mybatis.xml
代码语言:javascript
复制
<!-- 多数据源aop datasource -->
    <context:component-scan base-package="com.wangzhixuan.commons.datasource" />

    <!-- base dataSource -->
    <bean name="baseDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <property name="initialSize" value="5" />
        <property name="maxActive" value="100" />
        <property name="minIdle" value="10" />
        <property name="maxWait" value="60000" />
        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testOnBorrow" value="true" />
        <property name="testOnReturn" value="true" />
        <property name="testWhileIdle" value="true" />
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <property name="removeAbandoned" value="true" />
        <property name="removeAbandonedTimeout" value="1800" />
        <property name="logAbandoned" value="true" />
        <property name="filters" value="mergeStat" />
    </bean>

    <!-- 主库 -->
    <bean name="master-dataSource" parent="baseDataSource"
        init-method="init">
        <property name="url" value="${master_driverUrl}" />
        <property name="username" value="${master_username}" />
        <property name="password" value="${master_password}" />
    </bean>

    <!-- 从库 -->
    <bean name="slave-dataSource" parent="baseDataSource" init-method="init">
        <property name="url" value="${slave_driverUrl}" />
        <property name="username" value="${slave_username}" />
        <property name="password" value="${slave_password}" />
    </bean>
    
    <!--主从库选择 -->
    <bean id="dynamicDataSource" class="com.wangzhixuan.commons.datasource.DynamicDataSource">
        <property name="master" ref="master-dataSource" />
        <property name="slaves">
            <list>
                <ref bean="slave-dataSource" />
            </list>
        </property>
    </bean>

二、通用类

1.DataSourceAspect 切换到指定的数据源
代码语言:javascript
复制
package com.wangzhixuan.commons.datasource;

import com.wangzhixuan.commons.annotation.DataSourceChange;
import com.wangzhixuan.commons.exception.DataSourceAspectException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 有{@link com.wangzhixuan.commons.annotation.DataSourceChange}注解的方法,调用时会切换到指定的数据源
 *
 * @author tanghd
 */
@Aspect
@Component
public class DataSourceAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceAspect.class);

    @Around("@annotation(dataSourceChange)")
    public Object doAround(ProceedingJoinPoint pjp, DataSourceChange dataSourceChange) {
        Object retVal = null;
        boolean selectedDataSource = false;
        try {
            if (null != dataSourceChange) {
                selectedDataSource = true;
                if (dataSourceChange.slave()) {
                    DynamicDataSource.useSlave();
                } else {
                    DynamicDataSource.useMaster();
                }
            }
            retVal = pjp.proceed();
        } catch (Throwable e) {
            LOGGER.warn("数据源切换错误", e);
            throw new DataSourceAspectException("数据源切换错误", e);
        } finally {
            if (selectedDataSource) {
                DynamicDataSource.reset();
            }
        }
        return retVal;
    }
}
2.AbstractRoutingDataSource 配置主从数据源后,根据选择,返回对应的数据源
代码语言:javascript
复制
package com.wangzhixuan.commons.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 继承{@link org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource}
 * 配置主从数据源后,根据选择,返回对应的数据源。多个从库的情况下,会平均的分配从库,用于负载均衡。
 *
 * @author tanghd
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    private DataSource master; // 主库,只允许有一个
    private List<DataSource> slaves; // 从库,允许有多个
    private AtomicLong slaveCount = new AtomicLong();
    private int slaveSize = 0;

    private Map<Object, Object> dataSources = new HashMap<Object, Object>();

    private static final String DEFAULT = "master";
    private static final String SLAVE = "slave";

    private static final ThreadLocal<LinkedList<String>> datasourceHolder = new ThreadLocal<LinkedList<String>>() {

        @Override
        protected LinkedList<String> initialValue() {
            return new LinkedList<String>();
        }

    };

    /**
     * 初始化
     */
    @Override
    public void afterPropertiesSet() {
        if (null == master) {
            throw new IllegalArgumentException("Property 'master' is required");
        }
        dataSources.put(DEFAULT, master);
        if (null != slaves && slaves.size() > 0) {
            for (int i = 0; i < slaves.size(); i++) {
                dataSources.put(SLAVE + (i + 1), slaves.get(i));
            }
            slaveSize = slaves.size();
        }
        this.setDefaultTargetDataSource(master);
        this.setTargetDataSources(dataSources);
        super.afterPropertiesSet();
    }

    /**
     * 选择使用主库,并把选择放到当前ThreadLocal的栈顶
     */
    public static void useMaster() {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("use datasource :" + datasourceHolder.get());
        }
        LinkedList<String> m = datasourceHolder.get();
        m.offerFirst(DEFAULT);
    }

    /**
     * 选择使用从库,并把选择放到当前ThreadLocal的栈顶
     */
    public static void useSlave() {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("use datasource :" + datasourceHolder.get());
        }
        LinkedList<String> m = datasourceHolder.get();
        m.offerFirst(SLAVE);
    }

    /**
     * 重置当前栈
     */
    public static void reset() {
        LinkedList<String> m = datasourceHolder.get();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("reset datasource {}", m);
        }
        if (m.size() > 0) {
            m.poll();
        }
    }

    /**
     * 如果是选择使用从库,且从库的数量大于1,则通过取模来控制从库的负载,
     * 计算结果返回AbstractRoutingDataSource
     */
    @Override
    protected Object determineCurrentLookupKey() {
        LinkedList<String> m = datasourceHolder.get();
        String key = m.peekFirst() == null ? DEFAULT : m.peekFirst();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("currenty datasource :" + key);
        }
        if (null != key) {
            if (DEFAULT.equals(key)) {
                return key;
            } else if (SLAVE.equals(key)) {
                if (slaveSize > 1) {// Slave loadBalance
                    long c = slaveCount.incrementAndGet();
                    c = c % slaveSize;
                    return SLAVE + (c + 1);
                } else {
                    return SLAVE + "1";
                }
            }
            return null;
        } else {
            return null;
        }
    }

    public DataSource getMaster() {
        return master;
    }

    public List<DataSource> getSlaves() {
        return slaves;
    }

    public void setMaster(DataSource master) {
        this.master = master;
    }

    public void setSlaves(List<DataSource> slaves) {
        this.slaves = slaves;
    }

}
3.DataSourceChange 主从数据源切换注解
代码语言:javascript
复制
package com.wangzhixuan.commons.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceChange {
    boolean slave() default false;
}

三、在服务层调用时加入数据源标识,DataSourceChange注解,设置数据源为从库(这里只写出了接口的实现,dao和mapper,请自行添加)

代码语言:javascript
复制
package com.wangzhixuan.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.wangzhixuan.commons.annotation.DataSourceChange;
import com.wangzhixuan.mapper.SlaveMapper;
import com.wangzhixuan.service.SlaveService;

@Service
public class SlaveServiceImpl implements SlaveService {

    @Autowired
    private SlaveMapper slaveMapper;

    @Override
    @DataSourceChange(slave = true)
    public Integer count() {
        return slaveMapper.count();
    }

    @Override
    @DataSourceChange(slave = true)
    public Integer count2() {
        return slaveMapper.count2();
    }

}

四、测试类(这里是查询从库里的一张表的记录总数),将原来的主库切换到从库。

代码语言:javascript
复制
@Test
    public void testFindAllShop() {
        Integer count = slaveService.count();
        //Integer count2 = slaveService.count2();
        System.out.println("#######1:"+count);
    }
需要注意的一点:

@DataSourceChange(slave = true)只对service(业务)层起作用。

如果在测试类@Test下加是无作用的

代码语言:javascript
复制
@Test
@DataSourceChange(slave = true)
    public void testFindAllShop() {
        Integer count = slaveService.count();
        //Integer count2 = slaveService.count2();
        System.out.println("#######1:"+count);
    }
 博客转自:https://www.cnblogs.com/aegisada/p/5699058.html
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-12-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、配置文件
    • 1、jdbc.properties
      • 2、spring-mybatis.xml
      • 二、通用类
        • 1.DataSourceAspect 切换到指定的数据源
          • 2.AbstractRoutingDataSource 配置主从数据源后,根据选择,返回对应的数据源
            • 3.DataSourceChange 主从数据源切换注解
            • 三、在服务层调用时加入数据源标识,DataSourceChange注解,设置数据源为从库(这里只写出了接口的实现,dao和mapper,请自行添加)
            • 四、测试类(这里是查询从库里的一张表的记录总数),将原来的主库切换到从库。
              • 需要注意的一点:
                •  博客转自:https://www.cnblogs.com/aegisada/p/5699058.html
                相关产品与服务
                数据库
                云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档