在上一节中,我们讲述了多数据的情况:

1. 数据源不同(数据库厂商不同, 业务范围不同, 业务数据不同)

2. SQL mapper 文件不同,

3. mybatis + 数据方言不同

即最为简单的多数据, 将多个数据源叠加在一起,不同service---》dao--->sessionFactory;

如果上述的所有条件都相同,仅仅是数据的的多个拷贝情况,想做主备(读写分离),那我们当然可以用2套复制的代码和配置,但是显然不是最佳的实现方式。

这里,我们就讲解一下多数据源的读写分离,怎么实现。

首先,我们从读写分离的入口来看,  读read (对应JAVA/ getXXX,  queryXXX),   写write(对应updateXXX,  insertXXX),  如果能在这些方法执行时,自动切换到只读数据库/只写数据,那么就可以实现读写分离, 那我们自然可以想到spring 的AOP, 并以annotation的方式实现。

具体实现步骤如下:

1.   实现自定义的数据源注解: @DataSource("read")  / @DataSource("write")

 package com.robin.it.ds;

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

2.  实现一个数据库选择器ChooseDataSource

package com.robin.it.ds;

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

public class ChooseDataSource extends AbstractRoutingDataSource {

    @Override
protected Object determineCurrentLookupKey() {
return HandleDataSource.getDataSource();
} } ============
package com.robin.it.ds; public class HandleDataSource { public static final ThreadLocal<String> holder = new ThreadLocal<String>(); public static void putDataSource(String datasource) {
holder.set(datasource);
} public static String getDataSource() {
return holder.get();
}
}

3.   实现AOP 的的数据拦截,切换功能

package com.robin.it.ds;

import java.lang.reflect.Method;  

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
//@Aspect
//@Component
public class DataSourceAspect {
//@Pointcut("execution(* com.apc.cms.service.*.*(..))")
public void pointCut(){}; // @Before(value = "pointCut()")
public void before(JoinPoint point)
{
Object target = point.getTarget();
System.out.println(target.toString());
String method = point.getSignature().getName();
System.out.println(method);
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz[0].getMethod(method, parameterTypes);
System.out.println(m.getName());
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
System.out.println("==============================={}"+data.value());
HandleDataSource.putDataSource(data.value());
}else{
System.out.println("000000000000000000000");
} } catch (Exception e) {
e.printStackTrace();
}
}
}

 注意:  黄色高亮部分,利用反射读取 Interface的 的定义(包括annotation 的申明);动态读取参数,进行数据源切换;

4.  定义接口服务

package com.robin.it.permission.service;

import com.robin.it.ds.DataSource;
import com.robin.it.permission.module.User; /**
* Created by Robin.yi on 2015-4-11.
*/
public interface IUserService { @DataSource("read")
public User getUser(Integer userId); @DataSource("read")
public User getUser(String account, String password); @DataSource("write")
public void insertUser(User user);
}

主意:上面是决定走读库/写库的语句:   @DataSource("write")   /  @DataSource("read")

接口的实现无特别说明,此次省略。

5.  整个Spring 的applicationContent.xml  文件配置如下:

<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.1.xsd "> <description>
This file is used to define the authority info.
</description> <!-- 用于扫描 Spring 的各类annotation bean 变量:
@autowired ; @Resource
-->
<context:annotation-config/> <!-- 用于扫描类中组建:
如: @Controller, @Service, @Component
-->
<context:component-scan base-package="com.robin.it.permission.*">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan> <tx:annotation-driven /> <bean id="configProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="false" />
<property name="locations">
<list>
<!-- 这里支持多种寻址方式:classpath和file -->
<value>classpath:/database.properties</value>
<value>classpath:/config.properties</value>
<!-- 推荐使用file的方式引入,这样可以将配置和代码分离 -->
<!-- <value>file:/opt/demo/config/demo-message.properties</value>-->
</list>
</property>
</bean> <util:properties id="props" location="classpath:/config.properties"/> <!-- MYSQL 配置 --> <bean id="readDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${mysql.jdbc.driverClassName}</value></property>
<property name="url"><value>${mysql.jdbc.url}</value></property>
<property name="username"><value>${mysql.jdbc.username}</value></property>
<property name="password"><value>${mysql.jdbc.password}</value></property>
</bean> <bean id="writeDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${mysql2.jdbc.driverClassName}</value></property>
<property name="url"><value>${mysql2.jdbc.url}</value></property>
<property name="username"><value>${mysql2.jdbc.username}</value></property>
<property name="password"><value>${mysql2.jdbc.password}</value></property>
</bean> <bean id="dataSource" class="com.robin.it.ds.ChooseDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- write -->
<entry key="write" value-ref="writeDataSource"/>
<!-- read -->
<entry key="read" value-ref="readDataSource"/>
</map> </property>
<property name="defaultTargetDataSource" ref="writeDataSource"/>
</bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 指定sqlMapConfig总配置文件,订制的environment在spring容器中不在生效-->
<property name="configLocation" value="classpath:mybatis-config-mysql.xml"/>
<!--指定实体类映射文件,可以指定同时指定某一包以及子包下面的所有配置文件,mapperLocations和configLocation有一个即可,
当需要为实体类指定别名时,可指定configLocation属性,再在mybatis总配置文件中采用mapper引入实体类映射文件 -->
<property name="mapperLocations">
<list>
<value>classpath*:/mysqlmapper/*Mapper.xml</value>
</list>
</property>
</bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.robin.it.permission.dao" />
<!-- optional unless there are multiple session factories defined -->
<property name="sqlSessionFactoryBeanName" value="sessionFactory" />
</bean> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="dataSourceAspect" class="com.robin.it.ds.DataSourceAspect" /> <aop:config>
<aop:aspect id="c" ref="dataSourceAspect">
<aop:pointcut id="tx" expression="execution(* com.robin.it.permission.service.*.*(..))"/>
<aop:before pointcut-ref="tx" method="before"/>
</aop:aspect>
</aop:config>
</beans>

重点关注标为黄色的部分: 如何配置多数据源;  当知悉对应service  [com.robin.it.permission.service.*.*(..)] , 的相关方法之前(aop:before),  执行DataSourceAspect切面方法;

数据库配置文件database.properties 参考,上一章节。

到此,已完成多数据源的,读写分离配置。

Spring 数据源配置三:多数据源的更多相关文章

  1. spring+hibernate 配置多个数据源过程 以及 spring中数据源的配置方式

    spring+hibernate 配置多个数据源过程 以及 spring中数据源的配置方式[部分内容转载] 2018年03月27日 18:58:41 守望dfdfdf 阅读数:62更多 个人分类: 工 ...

  2. spring+mybatis配置多个数据源

    http://www.cnblogs.com/lzrabbit/p/3750803.html

  3. 【spring boot】12.spring boot对多种不同类型数据库,多数据源配置使用

    2天时间,终于把spring boot下配置连接多种不同类型数据库,配置多数据源实现! ======================================================== ...

  4. spring基于通用Dao的多数据源配置详解【ds1】

    spring基于通用Dao的多数据源配置详解 有时候在一个项目中会连接多个数据库,需要在spring中配置多个数据源,最近就遇到了这个问题,由于我的项目之前是基于通用Dao的,配置的时候问题不断,这种 ...

  5. spring基于通用Dao的多数据源配置

    有时候在一个项目中会连接多个数据库,须要在spring中配置多个数据源,近期就遇到了这个问题,因为我的项目之前是基于通用Dao的,配置的时候问题不断.这样的方式和资源文件冲突:扫描映射文件的话,Sql ...

  6. Springboot 多数据源配置,结合tk-mybatis

    一.前言 作为一个资深的CRUD工程师,我们在实际使用springboot开发项目的时候,难免会遇到同时使用多个数据库的情况,比如前脚刚查询mysql,后脚就要查询sqlserver. 这时,我们很直 ...

  7. 在Spring中配置SQL server 2000

    前言 Lz主要目的是在Spring中配置SQL server 2000数据库,但实现目的的过程中参差着许多SQL server 2000的知识,也包罗在本文记载下来!(Lz为什么要去搞sql serv ...

  8. 搞定SpringBoot多数据源(2):动态数据源

    目录 1. 引言 2. 动态数据源流程说明 3. 实现动态数据源 3.1 说明及数据源配置 3.1.1 包结构说明 3.1.2 数据库连接信息配置 3.1.3 数据源配置 3.2 动态数据源设置 3. ...

  9. Spring:(三) --常见数据源及声明式事务配置

    Spring自带了一组数据访问框架,集成了多种数据访问技术.无论我们是直接通过 JDBC 还是像Hibernate或Mybatis那样的框架实现数据持久化,Spring都可以为我们消除持久化代码中那些 ...

随机推荐

  1. MAT(2)安装Memory Analyzer

    http://www.eclipse.org/mat/ 两大功能: 1.find memory leaks 2.reduce memory consumption 安装步骤: 1. 打开 eclips ...

  2. Java NIO原理和使用(转载一)

    Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内 ...

  3. Java学习笔记(八):集合类

    Java中对数据的存储会使用到集合类,下面我们来看看Java中常用的集合类. Collection接口 集合的接口,可以简单的理解为可以动态扩充的数组. Collection接口定义了很多相关的方法, ...

  4. Unity3D之Mecanim动画系统学习笔记(二):模型导入

    我们要在Unity3D中使用上模型和动画,需要经过下面几个阶段的制作,下面以一个人形的模型开发为准来介绍. 模型制作 模型建模(Modelling) 我们的美术在建模时一般会制作一个称为T-Pose( ...

  5. VMware Workstation 11.0 官方中文版最强虚拟机软件(附下载地址)

    VMware Workstation 11.0 新版本功能一览: 支持 Windows 8.1 Update.Windows Server 2012 R2.Ubuntu 14.10.RHEL 7.Ce ...

  6. 极域电子教室 e-Learning Class V4 2010专业版 学生机 卸载方法

    学校的机房一般都会装教师机远程控制学生机的软件.比如我们学校装的就是"极域电子教室 e-Learning Class V4 2010专业版",上课的时候直接强制全屏远控,卸载又提示 ...

  7. 【HMTL】3D标签球

    这是一个3D TAG 在网站展示中是个不错的东东,能让人眼前一亮,值得收藏. 这个是效果: 源码下载: 点 击 下 载

  8. Oracle 生成随机密码

    需求:需要定期更改密码.要求是1.密码位数11位.2.必须包含大小写字母.数字.特殊字符.3.排除一些特殊字符如().@.& oracle数据库中有可已生成随机密码包dbms_random,但 ...

  9. &lt;Android&gt;关于EditText中setInputType和setSingleLine的冲突

    近期自己开发了一个带有删除button的EditText,一方面须要设置为SingleLine,还有一方面又须要设置输入类型,起先在xml文件里设置了android:inputType类型,在自己定义 ...

  10. Playing with ptrace, Part I

    X86_64 的 Redhat / Centos / Scientific 下面,若要编译.运行32位程序,需要安装以下包: yum install libgcc.i686 yum install g ...