一、配置文件

1、jdbc.properties

master_driverUrl=jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
master_username=root
master_password= slave_driverUrl=jdbc:mysql://localhost:3306/wechat?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
slave_username=root
slave_password=

2、spring-mybatis.xml

<!-- 多数据源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="" />
<property name="maxActive" value="" />
<property name="minIdle" value="" />
<property name="maxWait" value="" />
<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="" />
<property name="minEvictableIdleTimeMillis" value="" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="" />
<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 切换到指定的数据源

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 配置主从数据源后,根据选择,返回对应的数据源

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 = ; 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() > ) {
for (int i = ; i < slaves.size(); i++) {
dataSources.put(SLAVE + (i + ), 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() > ) {
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 > ) {// Slave loadBalance
long c = slaveCount.incrementAndGet();
c = c % slaveSize;
return SLAVE + (c + );
} else {
return SLAVE + "";
}
}
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 主从数据源切换注解

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,请自行添加)

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

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

@Test
public void testFindAllShop() {
Integer count = slaveService.count();
//Integer count2 = slaveService.count2();
System.out.println("#######1:"+count);
}

需要注意的一点:

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

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

@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

spring+spring mvc+mybatis 实现主从数据库配置的更多相关文章

  1. Unit03: Spring Web MVC简介 、 基于XML配置的MVC应用 、 基于注解配置的MVC应用

    Unit03: Spring Web MVC简介 . 基于XML配置的MVC应用 . 基于注解配置的MVC应用 springmvc (1)springmvc是什么? 是一个mvc框架,用来简化基于mv ...

  2. Spring JDBC主从数据库配置

    通过昨天学习的自定义配置注释的知识,探索了解一下web主从数据库的配置: 背景:主从数据库:主要是数据上的读写分离: 数据库的读写分离的好处? 1. 将读操作和写操作分离到不同的数据库上,避免主服务器 ...

  3. spring boot + druid + mybatis + atomikos 多数据源配置 并支持分布式事务

    文章目录 一.综述 1.1 项目说明 1.2 项目结构 二.配置多数据源并支持分布式事务 2.1 导入基本依赖 2.2 在yml中配置多数据源信息 2.3 进行多数据源的配置 三.整合结果测试 3.1 ...

  4. 2018-01-08 学习随笔 SpirngBoot整合Mybatis进行主从数据库的动态切换,以及一些数据库层面和分布式事物的解决方案

    先大概介绍一下主从数据库是什么?其实就是两个或N个数据库,一个或几个主负责写(当然也可以读),另一个或几个从只负责读.从数据库要记录主数据库的具体url以及BigLOG(二进制日志文件)的参数.原理就 ...

  5. CentOS7下Mysql5.7主从数据库配置

    本文配置主从使用的操作系统是Centos7,数据库版本是mysql5.7. 准备好两台安装有mysql的机器(mysql安装教程链接) 主数据库配置 每个从数据库会使用一个MySQL账号来连接主数据库 ...

  6. CentOS 6.6 中 mysql_5.6 主从数据库配置

    [mysql5.6 主从复制] 1.配置主从节点的服务配置文件 1.1.配置master节点 [mysqld] binlog-format=row log-bin=master-bin log-sla ...

  7. MySQL主从数据库配置与原理

    1.为什么要搭建主从数据库 (1)通过增加从库实现读写分离,提高系统负载能力 (2)将从库作为数据库备份库,实现数据热备份,为数据恢复提供机会 (3)根据业务将不同服务部署在不同机器同时又共享相同的数 ...

  8. spring mvc+mybatis+sql server简单配置

    context.xml配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&qu ...

  9. MAVEN构建Spring +Spring mvc + Mybatis 项目(Maven配置部分(workshop))

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

随机推荐

  1. Oracle中使用游标获取指定数据表的所有字段名对应的字符串

    操作步骤:打开PLSQL Developer后,直接执行下面的语句就可以出来 --Oracle中使用游标获取指定数据表的所有字段名对应的字符串 declare mytablename VARCHAR( ...

  2. XBMC源代码简析 5:视频播放器(dvdplayer)-解复用器(以ffmpeg为例)

    XBMC分析系列文章: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分析 4: ...

  3. Python学习笔记 - 字符串和编码

    #!/usr/bin/env python3 # -*- coding: utf-8 -*- #第一行注释是为了告诉Linux/OS X系统, #这是一个Python可执行程序,Windows系统会忽 ...

  4. 学习tornado:模板

    第一次接触tornado的template,是在做oastub的时候,因为一位同学在handler里面硬编码了html代码,我决定引入template机制来降低逻辑与页面的耦合. 简介 tornado ...

  5. ISLR系列:(1)线性回归 Linear Regression

       Linear Regression 此博文是 An Introduction to Statistical Learning with Applications in R 的系列读书笔记,作为本 ...

  6. AngularJS进阶(三十五)浏览器兼容性解决之道

    浏览器兼容性解决之道 前言 浏览器兼容性一直是前端开发中不得不面对的一个问题.而最突出的就是IE.对绝大多数公司来说,兼容IE6的性价比已经很低,而IE7则几乎已经绝迹.所以,常见的兼容性下限是IE8 ...

  7. 新手自定义view练习实例之(一) 泡泡弹窗

    转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50455412 本系列是为新手准备的自定义view练习项目(大牛请无视),相信在学习过程 ...

  8. (二十五)键盘的设置与TextField细节处理

    设置Return Key类型为Send并且勾选下面的复选框即可实现没有内容时禁用keyboard,有内容自动启用. 文本框左边框与文本留有间距的方法: //文本框左侧留下间距 UIView *left ...

  9. 移植Cocos2D到Android平台的原理

    幸运的,SpriteBuilder使得适配(安卓)多种多样的屏幕尺寸变得容易起来,因为Android Xcode插件允许你使用任何Cocos2D的特性并且可以继续使用很多iOS的框架(framewor ...

  10. AndroidBinder进程间通信系统-android学习之旅(86)

    目录 前言及知识准备 Service组件结构 Clinet组件结构 与Binder驱动程序交互 总结 Binder进程间通信实例 问题 本次主要介绍Android平台下Binder进程间通信库.所谓通 ...