spring 配置双数据源并读写分离

摘自 开源项目Ibase4j

   关键思想在于AbstractRoutingSource 类 还有方法名称和切入点去控制使用哪个数据源

   1.首先在配置文件配置多个数据源 并且交给继承自spring AbstractRoutingSource去管理

datasource.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter" lazy-init="true">
        <description>状态过滤器</description>
        <property name="slowSqlMillis" value="3000" />
        <property name="logSlowSql" value="true" />
        <property name="mergeSql" value="true" />
    </bean>
    <bean id="readDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close" init-method="init" lazy-init="true">
        <description>只读数据库连接</description>
        <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.reader.url}" />
        <property name="username" value="${db.reader.username}" />
        <property name="password" value="${db.reader.password}" />
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="${db.initialSize}" />
        <!-- 连接池最大数量 -->
        <property name="maxActive" value="${db.maxActive}" />
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="${db.minIdle}" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="${db.maxWait}" />
        <!-- -->
        <property name="defaultReadOnly" value="true" />
        <property name="proxyFilters">
            <list>
                <ref bean="stat-filter" />
            </list>
        </property>
        <property name="filters" value="${druid.filters}" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="validationQuery" value="SELECT ‘x‘" />
        <property name="timeBetweenLogStatsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}" />
    </bean>
    <bean id="writeDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close" init-method="init" lazy-init="true">
        <description>只写数据库连接</description>
        <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.writer.url}" />
        <property name="username" value="${db.writer.username}" />
        <property name="password" value="${db.writer.password}" />
        <property name="initialSize" value="${db.initialSize}" />
        <property name="maxActive" value="${db.maxActive}" />
        <property name="minIdle" value="${db.minIdle}" />
        <property name="maxWait" value="${db.maxWait}" />
        <property name="defaultReadOnly" value="false" />
        <property name="proxyFilters">
            <list>
                <ref bean="stat-filter" />
            </list>
        </property>
        <property name="filters" value="${druid.filters}" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="validationQuery" value="SELECT ‘x‘" />
        <property name="timeBetweenLogStatsMillis" value="60000" />
        <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}" />
        <property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}" />
    </bean>   <!--注意这里的配置-->
    <bean id="dataSource" class="org.ibase4j.core.aspect.ChooseDataSource" lazy-init="true">
        <description>数据源</description>
        <property name="targetDataSources">
            <map key-type="java.lang.String" value-type="javax.sql.DataSource">
                <!-- write -->
                <entry key="write" value-ref="writeDataSource" />
                <!-- read -->
                <entry key="read" value-ref="readDataSource" />
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="writeDataSource" />
        <property name="methodType">
            <map key-type="java.lang.String">
                <!-- read -->
                <entry key="read" value=",get,select,count,list,query," />
                <!-- write -->
                <entry key="write" value=",add,insert,create,update,delete,remove," />
            </map>
        </property>
    </bean>
    <!-- 注意这个切面 -->
    <bean class="org.ibase4j.core.aspect.DataSourceAspect" />
    <!--  -->
    <bean class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource" />
    </bean>
</beans>

接下来事继承类

package org.ibase4j.core.aspect;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 获取数据源
 *
 * @author ShenHuaJie
 * @version 2016年5月20日 下午3:17:16
 */
public class ChooseDataSource extends AbstractRoutingDataSource {
    public static Map<String, List<String>> METHODTYPE = new HashMap<String, List<String>>();

    // 获取数据源名称
    protected Object determineCurrentLookupKey() {
        return HandleDataSource.getDataSource();
    }

    // 设置方法名前缀对应的数据源
    public void setMethodType(Map<String, String> map) {
        for (String key : map.keySet()) {
            List<String> v = new ArrayList<String>();
            String[] types = map.get(key).split(",");
            for (String type : types) {
                if (StringUtils.isNotBlank(type)) {
                    v.add(type);
                }
            }
            METHODTYPE.put(key, v);
        }
    }
}

接下来切面实现类

package org.ibase4j.core.aspect;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * 切换数据源(不同方法调用不同数据源)
 *
 * @author ShenHuaJie
 * @version 2016年5月20日 下午3:17:52
 */
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DataSourceAspect {
    private final Logger logger = LogManager.getLogger();
    //配置切入点
    @Pointcut("execution(* org.ibase4j.service..*.*(..))")
    public void aspect() {
    }

    /**
     * 配置前置通知,使用在方法aspect()上注册的切入点
     */
    @Before("aspect()")
    public void before(JoinPoint point) {
        String className = point.getTarget().getClass().getName();
        String method = point.getSignature().getName();
        logger.info(className + "." + method + "(" + StringUtils.join(point.getArgs(), ",") + ")");
        try {
            L: for (String key : ChooseDataSource.METHODTYPE.keySet()) {
                for (String type : ChooseDataSource.METHODTYPE.get(key)) {
                    if (method.startsWith(type)) {
                        logger.info(key);
                        HandleDataSource.putDataSource(key);
                        break L;
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e);
            HandleDataSource.putDataSource("write");
        }
    }

    @After("aspect()")
    public void after(JoinPoint point) {
        HandleDataSource.clear();
    }
}
时间: 2025-01-05 05:54:58

spring 配置双数据源并读写分离的相关文章

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

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

Spring配置动态数据源-读写分离和多数据源

在现在互联网系统中,随着用户量的增长,单数据源通常无法满足系统的负载要求.因此为了解决用户量增长带来的压力,在数据库层面会采用读写分离技术和数据库拆分等技术.读写分离就是就是一个Master数据库,多个Slave数据库,Master数据库负责数据的写操作,slave库负责数据读操作,通过slave库来降低Master库的负载.因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.我们通常的做法

Spring+Mybatis实现主从数据库读写分离

Spring+Mybatis实现主从数据库读写分离 采用配置+注解的方式. 自定义@DataSource注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME

Spring配置DataSource数据源

在Spring框架中有如下3种获得DataSource对象的方法: 1.从JNDI获得DataSource. 2.从第三方的连接池获得DataSource. 3.使用DriverManagerDataSource获得DataSource. 一.从JNDI获得DataSource SpringJNDI数据源配置信息: <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean&qu

Spring配置c3p0数据源时出错报:java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector

今天在使用Spring配置c3p0数据源时,使用的数据库是mysql,服务器是tomcat,运行时报了一个 java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector 网络上找了很久都没有解决,最后发现是因为:         C3P0 少了个 jar 包,mchange-commons-java-0.2.3.1.jar 因为c3p0.jar依赖另一个jar包,就是mchange-commons-java-0.2.3.1.jar.

spring 配置多数据源(mysql读写分离)

前段时间刚换了家新公司,然后看项目代码里用了数据库读写分离的架构,然后好奇扒了代码简单看了下,总体来说就是运用spring aop切面方式来实现的.看明白后就在自己的个人小项目里运用了下,测试OK,所以下面总结下流程: 1.首先定义一个数据源注解,它有两个值,一个对应写库(主库),一个对应读库(从库) package com.jdd.ds; import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE})

161920、使用Spring AOP实现MySQL数据库读写分离案例分析

一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离案

使用Spring AOP实现MySQL数据库读写分离案例分析

一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离案

spring学习笔记(19)mysql读写分离后端AOP控制实例

在这里,我们接上一篇文章,利用JNDI访问应用服务器配置的两个数据源来模拟同时操作不同的数据库如同时操作mysql和oracle等.实际上,上个例子可能用来模拟mysql数据库主从配置读写分离更贴切些.既然如此,在本例中,我们就完成读写分离的模拟在web端的配置实例. 续上次的例子,关于JNDI数据源的配置和spring datasource的配置这里不再重复.下面着重加入AOP实现DAO层动态分库调用.可先看上篇文章<spring学习笔记(18)使用JNDI模拟访问应用服务器多数据源实例 >