Spring 数据源配置三:多数据源

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

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")

 1 package com.robin.it.ds;
 2
 3 import java.lang.annotation.Retention;
 4 import java.lang.annotation.Target;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.ElementType;
 7 /**
 8  * RUNTIME
 9  * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
10  * @author Robin.yi
11  *
12  */
13
14 @Retention(RetentionPolicy.RUNTIME)
15 @Target(ElementType.METHOD)
16 public @interface DataSource {
17        String value();
18 }

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 参考,上一章节。

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

时间: 2025-01-15 21:24:38

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

spring hibernate配置切换数据源,实现读写分离

spring的配置如下 <!-- 主数据源--> <bean id="masterDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <prope

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

有时候在一个项目中会连接多个数据库,须要在spring中配置多个数据源,近期就遇到了这个问题,因为我的项目之前是基于通用Dao的,配置的时候问题不断.这样的方式和资源文件冲突:扫描映射文件的话,SqlSessionFactory的bean名字必须是sqlSessionFactory 他读不到sqlSessioNFactory2或者其它名字,终于解决方法例如以下: 1.在项目中增加例如以下类MultipleDataSource.java package com.etoak.util; import

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

spring基于通用Dao的多数据源配置详解 有时候在一个项目中会连接多个数据库,需要在spring中配置多个数据源,最近就遇到了这个问题,由于我的项目之前是基于通用Dao的,配置的时候问题不断,这种方式和资源文件冲突:扫描映射文件的话,SqlSessionFactory的bean名字必须是sqlSessionFactory 他读不到sqlSessioNFactory2或者其他名字,最终解决方法如下: 1.在项目中加入如下类MultipleDataSource.java ? 1 2 3 4 5

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

目录 1. 引言 2. 动态数据源流程说明 3. 实现动态数据源 3.1 说明及数据源配置 3.1.1 包结构说明 3.1.2 数据库连接信息配置 3.1.3 数据源配置 3.2 动态数据源设置 3.2.1 动态数据源配置 3.2.2 动态选择数据源 3.2.3 动态数据源使用 3.3 使用 AOP 选择数据源 3.3.1 定义数据源注解 3.3.2 定义数据源切面 3.3.3 使用 AOP 进行数据源切换 4. 再思考一下 5. 总结 参考资料 往期文章 一句话概括:使用动态数据源对多个数据库

复习Spring第三课--数据源配置的多种方式

spring数据源配置可以说分为:spring容器自带连接池.项目中创建连接池.服务器创建连接池三种 一.spring容器自带连接池   Spring本身也提供了一个简单的数据源实现类DriverManagerDataSource ,它位于org.springframework.jdbc.datasource包中.这个类实现了javax.sql.DataSource接口,但 它并没有提供池化连接的机制,每次调用getConnection()获取新连接时,只是简单地创建一个新的连接.因此,这个数据

Spring+Mybatis多数据源配置

一.配置文件 properties ds1.driverClassName=com.mysql.jdbc.Driver ds1.url=jdbc:mysql://192.168.200.130:3306/test1?useUnicode=true&characterEncoding=UTF-8 ds1.username=hhh ds1.password=123456 ds2.driverClassName=oracle.jdbc.OracleDriver ds2.url=jdbc:mysql:/

spring(16)------spring的数据源配置

在spring中,通过XML的形式实现数据源的注入有三种形式. 一,使用spring自带的DriverManagerDataSource 使用DriverManagerDataSource配置数据源与直接使用jdbc在效率上没有多大的区别,使用DriverManagerDataSource配置数据源 的代码实例如下,这里重点研究spring的数据源配置,就采用spring编程式事务处理来来研究数据源的配置. 所需要的jar包和spring编程式配置:http://blog.csdn.net/yh

在Tomcat配置JNDI数据源的三种方式

在Tomcat配置JNDI数据源的三种方式 分类: java进阶2012-07-01 10:24 18328人阅读 评论(0) 收藏 举报 tomcatjdbcweblogicmysqlbean测试 目录(?)[+] 在我过去工作的过程中,开发用服务器一般都是Tomcat 数据源的配置往往都是在applicationContext.xml中配置一个dataSource的bean 然后在部署时再修改JNDI配置 我猜是因为Tomcat的配置需要改配置文件 不像JBoss,Weblogic等服务器在

SSH框架系列:Spring配置多个数据源

问题:有开源框架mysql的 ,还有旧系统 sqlserver2000的,解决这些问题总有些成长. 解决sqlserver安装环境:http://qdh68.blog.163.com/blog/static/13756126201261742437357/ 别说sqlserver2000不怎么样,最起码那友好的管理叫你明白数据库. 2.  先说配置jdbc:如果sqlserver 2000以上还好 找到jar包 ,按目录加载到maven仓库,安装一下 http://outofmemory.cn/