SSM多数据源和动态数据源切换

公司最近要我写一个动态数据源的切换,使得用户能在前台选择一些库动态的进行切换,查询不同库不同模块下的数据信息,数据源连接库的信息都在Mysql数据库里面保存(ip,端口,username和passsword等)
由于之前的框架是Spring框架,公司新业务要求用在SSM下也能做一个动态数据源的切换,几天下来,终于在某些方面有了一点点突破.
在这里先要想明白一些问题:
1.多数据源指的什么
2.动态数据源指的什么
3.在什么时候切换数据源(业务层面先不说,要知道在框架中什么位置切)
4.切数据源的时候,怎么去取未知的数据源信息

下面我会从单、多数据源的角度去解决不同情况下的数据源切换问题!

想明白了以上三个问题之后,其实大概思路就有了,在解决我这个问题的层次,按照多数据源做动态切换是可以解决的,但是,单数据源也是可以做到动态数据源切换的,这里就看各位的实质需求了,目前来说,暂时没有觉得和多数据源动态切换有区别,只是控制上多了一层AOP的控制!还望各位赐教

单数据源和多数据源:
1.单数据源:在配置文件中只能看到一个datasource配置
2.多数据源:除了一个datasource数据源,还存在其他的数据源,这种情况的配置就需要我们去控制

为了方便大家理解,我先从多数据源的角度去分析问题

我先贴出SSM框架中2个数据源的配置(网上借鉴的知识)
这里写图片描述
在这里我们可以看到,多数据源在配置文件中的写法无非就是把单数据源的配置拷贝n份,达到多数据源的目的.接下来就简单了,我们只需要在业务需求的时候,人为的去指定特定业务下该使用哪个数据源.
针对以上的多数据源配置,有以下一些情况可能发生:
1.多数据源的配置都已经写在配置文件中(如上图)
2.有一个或者多个确定的数据源,但是存在另外一个数据源的信息,是动态从数据库中查的(也就是我说的动态数据源),是不确定的.
针对上面的第二种情况,我们将上图中数据源datasource2里面的配置信息做一些调整,只改变bean配置,其他的datasource2事物和mybatis配置信息什么的不变:

<!--动态数据源设置-->
<bean id="datasource2"class="com.dsg.common.datasource.DynamicDataSource">
        <!--将数据源列表注入到属性_targetDataSources中去-->
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                //动态查的,此处先配置为默认
            </map>
        </property>
    <property name="defaultTargetDataSource" ref="mysqlDataSource"></property>
</bean>

com.dsg.common.datasource.DynamicDataSource这个类做的事情,就是要查询动态数据源的信息,并返回一个datasource给我们来操作特定的dao:

package com.dsg.dbmonitor.common.datasource;

import com.dsg.dbmonitor.common.datasource.vo.DynamicDatasourceVo;
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;

/**
 * Created by DSG-LiuLei on 2017/3/17.
 * spring的AbstractRoutingDataSource类来进行拓展多数据源
 * 类中的determineTargetDataSource是关键方法
 * 该方法用于返回我们使用的数据源
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static String
            MysqlDriverClassName = "com.mysql.jdbc.Driver",
            OracleDriverClassName = "oracle.jdbc.driver.OracleDriver";
    private static ApplicationContext context;
    private Map<Object, Object> _targetDataSources;
    private Connection defaultConn ;
    static {
        context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    }

    /**
     * 确定当前选择的数据源key
     * <p>
     * key由当前线程{@link DynamicDataSourceContext#set(String)}
     */
    @Override
    protected Object determineCurrentLookupKey() {

        String dataSourceName = DynamicDataSourceContext.get();

        System.out.println("--------BEGIN-my----------------------------------> use datasource " + dataSourceName);
        if (dataSourceName == null ) {//mysql设置为当前默认数据源
            dataSourceName = "mysql";
        }else{//动态数据源(从默认库中查询数据源信息)
            this.selectDataSource(dataSourceName);
        }
        return dataSourceName;
    }


    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        this._targetDataSources = targetDataSources;
        super.setTargetDataSources(this._targetDataSources);
        afterPropertiesSet();
    }

    public void addTargetDataSource(String key, BasicDataSource dataSource) {
        this._targetDataSources.put(key, dataSource);
        this.setTargetDataSources(this._targetDataSources);
    }

    public BasicDataSource createDataSource(String driverClassName, String url,
                                            String username, String password) {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setTestWhileIdle(true);
        return dataSource;
    }

    /**
     * @param serverId
     * @describe 数据源存在时不做处理,不存在时创建新的数据源链接,并将新数据作为当前数据源
     */
    public void selectDataSource(String serverId) {
        if ("mysql".equals(serverId + "")){
            DynamicDataSourceContext.set("mysql");
            return;
        }
        System.out.println(" function selectDataSource (1)  ::"+serverId);
        Object obj = this._targetDataSources.get(serverId);//1004
        if (obj != null ) {
            System.out.println(" function selectDataSource =(1)  obj::"+obj+" sid :::"+obj.toString());
            return;
        } else {
            System.out.println(" function selectDataSource  (2)  ::"+serverId);
            BasicDataSource dataSource = this.getDataSourceByDBTable(serverId);
            if (null != dataSource)
                this.setDataSource(serverId, dataSource);
        }
    }

    /**
     * @describe 查询serverId对应的数据源记录
     * @param beanKey
     *    @return
     */
    public BasicDataSource getDataSourceByDBTable(String  beanKey) {
        PreparedStatement preparedStatement =null;
        ResultSet resultSet =null;
        BasicDataSource dataSource = null;
        try{
            defaultConn = ((DataSource)context.getBean("mysqlDataSource")).getConnection();
            String sql = "select * from op_store_config_tb where id = '"+beanKey+"'";
            preparedStatement = defaultConn.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            Map<String,String> map = new HashMap<String, String>();
            DynamicDatasourceVo dynamicDatasourceVo = new DynamicDatasourceVo();
            while(resultSet.next()){
                dynamicDatasourceVo.setId(resultSet.getString("id"));
                dynamicDatasourceVo.setIp_address(resultSet.getString("ip_address"));//ip_address
                dynamicDatasourceVo.setTns_port(resultSet.getString("connect_port"));//port
                dynamicDatasourceVo.setTns_name(resultSet.getString("connect_name"));//sid
                dynamicDatasourceVo.setUser_name(resultSet.getString("user_name"));
                dynamicDatasourceVo.setPass_word(resultSet.getString("pass_word"));
            }
            //设定到以下的数据源当中去,当前是测试数据
            String url = "jdbc:oracle:thin:@"+dynamicDatasourceVo.getIp_address().trim()+
                    ":"+dynamicDatasourceVo.getTns_port()+
                    ":"+dynamicDatasourceVo.getTns_name().trim();
            System.out.println("公共库数据库查询数据源信息结果map--->"+url);
            dataSource = this.createDataSource(
                    OracleDriverClassName,
                    url,
                    dynamicDatasourceVo.getUser_name(),
                    dynamicDatasourceVo.getPass_word());
            System.out.println(dataSource.getDriverClassName()+","+dataSource.getUsername()+
                    ","+dataSource.getPassword()+","+dataSource.getUrl());
        }catch (Exception e){
            System.out.println("数据源DataSource创建失败!");
            e.printStackTrace();
        }finally {
            //释放资源,注意倒着释放
            if(resultSet!=null){
                try{
                    resultSet.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(preparedStatement!=null){
                try{
                    preparedStatement.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(defaultConn!=null){
                try{
                    defaultConn.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return dataSource;
    }

    /**
     * @param serverId
     * @param dataSource
     */
    public void setDataSource(String serverId, BasicDataSource dataSource) {
        this.addTargetDataSource(serverId + "", dataSource);
        DynamicDataSourceContext.set(serverId + "");
    }

    public static void main(String[] args) {
        Connection con = null;// 创建一个数据库连接
        PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
        ResultSet result = null;// 创建一个结果集对象
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
            System.out.println("开始尝试连接数据库!");
            String url = "jdbc:oracle:" + "thin:@192.168.1.251:1521:db11";// 127.0.0.1是本机地址,XE是精简版Oracle的默认数据库名
            String user = "dsg";// 用户名,系统默认的账户名
            String password = "dsg";// 你安装时选设置的密码
            con = DriverManager.getConnection(url, user, password);// 获取连接
            System.out.println("连接成功!");
            String sql = "select * from accessories";// 预编译语句,“?”代表参数
            pre = con.prepareStatement(sql);// 实例化预编译语句
            result = pre.executeQuery();// 执行查询,注意括号中不需要再加参数
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 逐一将上面的几个对象关闭,因为不关闭的话会影响性能、并且占用资源
                // 注意关闭的顺序,最后使用的最先关闭
                if (result != null)
                    result.close();
                if (pre != null)
                    pre.close();
                if (con != null)
                    con.close();
                System.out.println("数据库连接已关闭!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

无论是单个,还是多个数据源,我们不能避免的一点是,如何去取这个信息,一般来说,我们取出这个数据源的信息是为了更好的去切,也就是说,只有先取到值,才能切,从这个层面上来说,存放多数据源信息的表,是放在当前的数据源下就能查询到的!也就是我们说的默认数据源!
DynamicDataSource.java类中我就是这么做的,如何获取连接的conn?我用的老方法,暂时没找到合适的方法….有点遗憾,不过也算是实现了
以上的话,大概来说,多数据源就这两种情况了,确定的数据源和不确定的数据源两种.

Mybatis中还有另外一种单数据源管理多种类型的数据源(包括动态数据源)的配置方法,也是我解决现任务个人感觉较好的一种配置:
直接贴代码了就:

<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

    <tx:annotation-driven />
    <context:property-placeholder location="classpath:db.properties" />

    <bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
        <property name="slowSqlMillis" value="1000" />
        <property name="logSlowSql" value="true" />
        <property name="mergeSql" value="true" />
    </bean>

    <bean id="mysqlDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url">
            <value>${jdbc.url}</value>
        </property>
        <property name="username">
            <value>${jdbc.username}</value>
        </property>
        <property name="password">
            <value>${jdbc.password}</value>
        </property>
        <property name="initialSize">
            <value>${jdbc.initialSize}</value>
        </property>
        <property name="maxActive">
            <value>${jdbc.maxActive}</value>
        </property>
        <property name="proxyFilters">
            <list>
                <ref bean="stat-filter" />
            </list>
        </property>
    </bean>
    <bean id="oracleDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url">
            <value>${jdbc2.url}</value>
        </property>
        <property name="username">
            <value>${jdbc2.username}</value>
        </property>
        <property name="password">
            <value>${jdbc2.password}</value>
        </property>
        <property name="initialSize">
            <value>${jdbc2.initialSize}</value>
        </property>
        <property name="maxActive">
            <value>${jdbc2.maxActive}</value>
        </property>
        <property name="proxyFilters">
            <list>
                <ref bean="stat-filter" />
            </list>
        </property>
    </bean>

    <!--动态数据源设置-->
    <bean id="dynamicDataSource" class="com.dsg.common.datasource.DynamicDataSource">
        <!--将数据源列表注入到属性_targetDataSources中去-->
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="mysql" value-ref="mysqlDataSource" />
                <entry key="oracle" value-ref="oracleDataSource" />
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="mysqlDataSource"></property>
    </bean>


    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dynamicDataSource" />
        </property>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis.xml" ></property>
        <property name="dataSource" ref="dynamicDataSource" />
        <property name="mapperLocations">
            <array>
                <value>classpath:com/dsg/common/datasource/dao/*.xml</value>
                <value>classpath:com/dsg/dmp/dao/*.xml</value>
                <value>classpath:com/dsg/dbmonitor/dao/*.xml</value>
            </array>
        </property>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dsg.common.datasource.dao,com.dsg.dmp.dao,com.dsg.dbmonitor.dao" />
    </bean>
</beans>

从上面可以看到,我指定了一个dynamicDataSource去管理所有的数据源,固定的配置中,有mysql和oracle的数据源配置,是写死的,统一放在dynamicDataSource中去管理,那么我如何去实现动态数据源,
假设我调用DynamicDataSourceContext .set(“mysql”)
和DynamicDataSourceContext .set(“oracle”),是可以直接切换到对应的数据源下面的,但是动态数据源,会找不到,怎么办?
去DynamicDataSource类下的determineCurrentLookupKey()中做处理,如果查询不到配置文件中已经存在数据源key值,那么我们就动态的去创建一个数据源,并且指定它为当前查询的数据源:

package com.dsg.dbmonitor.common.datasource;

import com.dsg.dbmonitor.common.datasource.vo.DynamicDatasourceVo;
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;

/**
 * Created by DSG-LiuLei on 2017/3/17.
 * spring的AbstractRoutingDataSource类来进行拓展多数据源
 * 类中的determineTargetDataSource是关键方法
 * 该方法用于返回我们使用的数据源
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static String
            MysqlDriverClassName = "com.mysql.jdbc.Driver",
            OracleDriverClassName = "oracle.jdbc.driver.OracleDriver";
    private static ApplicationContext context;
    private Map<Object, Object> _targetDataSources;
    private Connection defaultConn ;
    static {
        context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
    }

    /**
     * 确定当前选择的数据源key
     * <p>
     * key由当前线程{@link DynamicDataSourceContext#set(String)}
     */
    @Override
    protected Object determineCurrentLookupKey() {

        String dataSourceName = DynamicDataSourceContext.get();

        System.out.println("--------BEGIN-my----------------------------------> use datasource " + dataSourceName);
        if (dataSourceName == null ) {//mysql设置为当前默认数据源
            dataSourceName = "mysql";
        }else{//动态数据源(从默认库中查询数据源信息)
            this.selectDataSource(dataSourceName);
        }
        return dataSourceName;
    }


    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        this._targetDataSources = targetDataSources;
        super.setTargetDataSources(this._targetDataSources);
        afterPropertiesSet();
    }

    public void addTargetDataSource(String key, BasicDataSource dataSource) {
        this._targetDataSources.put(key, dataSource);
        this.setTargetDataSources(this._targetDataSources);
    }

    public BasicDataSource createDataSource(String driverClassName, String url,
                                            String username, String password) {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setTestWhileIdle(true);
        return dataSource;
    }

    /**
     * @param serverId
     * @describe 数据源存在时不做处理,不存在时创建新的数据源链接,并将新数据作为当前数据源
     */
    public void selectDataSource(String serverId) {
        if ("mysql".equals(serverId + "")){
            DynamicDataSourceContext.set("mysql");
            return;
        }
        System.out.println(" function selectDataSource (1)  ::"+serverId);
        Object obj = this._targetDataSources.get(serverId);//1004
        if (obj != null ) {
            System.out.println(" function selectDataSource =(1)  obj::"+obj+" sid :::"+obj.toString());
            return;
        } else {
            System.out.println(" function selectDataSource  (2)  ::"+serverId);
            BasicDataSource dataSource = this.getDataSourceByDBTable(serverId);
            if (null != dataSource)
                this.setDataSource(serverId, dataSource);
        }
    }

    /**
     * @describe 查询serverId对应的数据源记录
     * @param beanKey
     *    @return
     */
    public BasicDataSource getDataSourceByDBTable(String  beanKey) {
        PreparedStatement preparedStatement =null;
        ResultSet resultSet =null;
        BasicDataSource dataSource = null;
        try{
            defaultConn = ((DataSource)context.getBean("mysqlDataSource")).getConnection();
            String sql = "select * from op_store_config_tb where id = '"+beanKey+"'";
            preparedStatement = defaultConn.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            Map<String,String> map = new HashMap<String, String>();
            DynamicDatasourceVo dynamicDatasourceVo = new DynamicDatasourceVo();
            while(resultSet.next()){
                dynamicDatasourceVo.setId(resultSet.getString("id"));
                dynamicDatasourceVo.setIp_address(resultSet.getString("ip_address"));//ip_address
                dynamicDatasourceVo.setTns_port(resultSet.getString("connect_port"));//port
                dynamicDatasourceVo.setTns_name(resultSet.getString("connect_name"));//sid
                dynamicDatasourceVo.setUser_name(resultSet.getString("user_name"));
                dynamicDatasourceVo.setPass_word(resultSet.getString("pass_word"));
            }
            //设定到以下的数据源当中去,当前是测试数据
            String url = "jdbc:oracle:thin:@"+dynamicDatasourceVo.getIp_address().trim()+
                    ":"+dynamicDatasourceVo.getTns_port()+
                    ":"+dynamicDatasourceVo.getTns_name().trim();
            System.out.println("公共库数据库查询数据源信息结果map--->"+url);
            dataSource = this.createDataSource(
                    OracleDriverClassName,
                    url,
                    dynamicDatasourceVo.getUser_name(),
                    dynamicDatasourceVo.getPass_word());
            System.out.println(dataSource.getDriverClassName()+","+dataSource.getUsername()+
                    ","+dataSource.getPassword()+","+dataSource.getUrl());
        }catch (Exception e){
            System.out.println("数据源DataSource创建失败!");
            e.printStackTrace();
        }finally {
            //释放资源,注意倒着释放
            if(resultSet!=null){
                try{
                    resultSet.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(preparedStatement!=null){
                try{
                    preparedStatement.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(defaultConn!=null){
                try{
                    defaultConn.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return dataSource;
    }

    /**
     * @param serverId
     * @param dataSource
     */
    public void setDataSource(String serverId, BasicDataSource dataSource) {
        this.addTargetDataSource(serverId + "", dataSource);
        DynamicDataSourceContext.set(serverId + "");
    }

    public static void main(String[] args) {
        Connection con = null;// 创建一个数据库连接
        PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
        ResultSet result = null;// 创建一个结果集对象
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
            System.out.println("开始尝试连接数据库!");
            String url = "jdbc:oracle:" + "thin:@192.168.1.251:1521:db11";// 127.0.0.1是本机地址,XE是精简版Oracle的默认数据库名
            String user = "dsg";// 用户名,系统默认的账户名
            String password = "dsg";// 你安装时选设置的密码
            con = DriverManager.getConnection(url, user, password);// 获取连接
            System.out.println("连接成功!");
            String sql = "select * from accessories";// 预编译语句,“?”代表参数
            pre = con.prepareStatement(sql);// 实例化预编译语句
            result = pre.executeQuery();// 执行查询,注意括号中不需要再加参数
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 逐一将上面的几个对象关闭,因为不关闭的话会影响性能、并且占用资源
                // 注意关闭的顺序,最后使用的最先关闭
                if (result != null)
                    result.close();
                if (pre != null)
                    pre.close();
                if (con != null)
                    con.close();
                System.out.println("数据库连接已关闭!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

也就是说DynamicDataSourceContext.set(serverId)中serverId如果不存在,我们就利用这个serverId去查询默认数据源下的动态数据源信息,重新建立一个数据源作为连接.本质上,还是一个单数据源,只不过配置文件中写了多个而已.

这样新的问题又出来了,我发现,一但切了数据源,当前数据源不在是默认的了,那我在查默认库数据还得切回来,好麻烦,怎么办,去配置文件里面指定?这种情况是不能指定dao去配置的,因为真实存在的数据源其实只有一个,那就是dynamicDataSource,只不过它管理了多个数据源的信息而已,当前用到的只有一个,那么我们在编写复杂业务的时候,多种数据源来回切换的需求是存在的,这么写岂不是很麻烦,Spring的aop切面可以帮我们处理这个问题,通过切面的前置和后置通知,我们可以统一处理这种重复的问题:

package com.dsg.common.datasource.aop;

import com.dsg.common.datasource.DynamicDataSourceContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/** * Created by DSG-LiuLei on 2017/3/19. * 数据源动态切换做不同web子项目之间的切入 */
@Aspect
@Component
public class DbAop {
    public DbAop(){

    }

    //项目1数据源切入
    @Pointcut("execution(* com.web.pack.service.impl.*.*(..))")
    private void anyDmpMethod() {}

    @Before("anyDmpMethod()")
    public void doBefore(JoinPoint joinPoint) throws Exception {
        System.out.println("dmp切面方法............");
        //dmp默认的是mysql数据源
        DynamicDataSourceContext.set("mysql");
    }



    //项目2数据源切入
    @Pointcut("execution(* com.web.pack2.dao.*.*(..))")
    private void anyDbmonitorMethod() {}

    @Before("anyDbmonitorMethod()")
    public void doswitchDs(JoinPoint joinPoint) throws Exception {
        System.out.println("Dbmonitor切面方法............");
        //测试Oracle环境
        DynamicDataSourceContext.set("oracle");
    }

}

这样我们只需要在编写代码的时候,mysql和oracle情况下,动态查询并切换数据源的各种业务做简单的分包处理,利用切面,统一切换数据源,就完美解决了!

package com.dsg.dbmonitor.common.aop;

import com.dsg.dbmonitor.common.datasource.DynamicDataSourceContext;
import com.dsg.dbmonitor.common.filter.SessionContext;
import com.dsg.dbmonitor.utils.MapUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Created by DSG-LiuLei on 2017/3/19.
 * 数据源动态切换做不同web子项目之间的切入
 */
@Aspect
@Component
public class DbAop {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //Dbmonitor mysql数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.dbmysql.*.*(..))")
    private void anyDbmonitorMethod1() {
    }

    @Before("anyDbmonitorMethod1()")
    public void doswitchDs1(JoinPoint joinPoint) throws Exception {
        DynamicDataSourceContext.set("mysql");
    }

    //Dbmonitor mysql数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.common.*.*(..))")
    private void anyDbmonitorMethodd() {
    }

    @Before("anyDbmonitorMethodd()")
    public void doswitchDsd(JoinPoint joinPoint) throws Exception {
        DynamicDataSourceContext.set("mysql");
    }

    //Dbmonitor mysql数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.user.*.*(..))")
    private void anyDbmonitorMethodddd() {
    }

    @Before("anyDbmonitorMethodddd()")
    public void doswitchDsddd(JoinPoint joinPoint) throws Exception {
        DynamicDataSourceContext.set("mysql");
    }







    //Dbmonitor oracle数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.dynamicoracle.*.*(..))")
    private void anyDbmonitorMethoddddd() {
    }

    @Before("anyDbmonitorMethoddddd()")
    public void doswitchDsdddd(JoinPoint joinPoint) throws Exception {
        if(joinPoint.getArgs().length>0){
            String ip = "";
            List<Object> list = Arrays.asList(joinPoint.getArgs());
            ip = String.valueOf(MapUtil.objectToMap(list.get(0)).get("ip"));
            if (!"null".equals(ip) && ip != null) {
                List<Map<String ,Object>> dslist =
                        this.jdbcTemplate.queryForList("SELECT id FROM op_store_config_tb" +
                                " WHERE ip_address = '"+ip+"'");
                DynamicDataSourceContext.set(String.valueOf(dslist.get(0).get("id")));
                System.out.println("'''''''''''''''''''''''''''''"+ip+"-"+String.valueOf(dslist.get(0).get("id")));
            }
        }
    }

    //Dbmonitor Oracl数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.systool.*.*(..))")
    private void anyDbmonitorMethoddd() {
    }

    @Before("anyDbmonitorMethoddd()")
    public void doswitchDsdd(JoinPoint joinPoint) throws Exception {
        //测试Oracle环境
        changeDataSource();//切动态数据源
    }

    //Dbmonitor oracle数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.dbmanager.*.*(..))")
    private void anyDbmonitorMethod() {
    }

    @Before("anyDbmonitorMethod()")
    public void doswitchDs(JoinPoint joinPoint) throws Exception {
        //测试Oracle环境
        changeDataSource();//切动态数据源
    }

    //Dbmonitor数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.systool.*.*(..))")
    private void anyDbmonitorMethod2() {
    }

    @Before("anyDbmonitorMethod2()")
    public void doswitchDs2(JoinPoint joinPoint) throws Exception {
        //测试Oracle环境
        changeDataSource();//切动态数据源
    }

    //Dbmonitor数据源切入
    @Pointcut("execution(* com.dsg.dbmonitor.dao.dbmonitor.*.*(..))")
    private void anyDbmonitorMethod3() {
    }

    @Before("anyDbmonitorMethod3()")
    public void doswitchDs3(JoinPoint joinPoint) throws Exception {
        //测试Oracle环境
        changeDataSource();//切动态数据源
    }

    public void changeDataSource(){
        HttpSession session = SessionContext.getSession();
        DynamicDataSourceContext.set(String.valueOf(session.getAttribute("DATASOURCE")));
    }
}

知识点补充:
DynamicDataSource extends AbstractRoutingDataSource
这个我自定义的类去继承AbstractRoutingDataSource的原因:
1.重写determineCurrentLookupKey():
触发时机:切换数据源会自动进入此方法
作用:纳入我们动态数据源切换的需求
2.重写setTargetDataSources()
3.DynamicDataSourceContext.get()这个方法,是取出当前线程的数据源的key值,根据这个key值,去查_targetDataSources这个map,其实也相当于缓存,用户在切换数据源时,会传一个id,去map中查询,如果查到了记录,就说明已经存在数据源了,不需要去查库.

之前在问到写过一篇,这一篇是根据之前有些网友对我写的博客的优化建议,所以对之前写的有了一些补充!
SSM多数据源动态切换老地址
其他的方法都只是辅助而已!如果有遇到这种问题的小伙伴,可以加我qq1507371615,一般都在线!有说的不对的地方,欢迎大家指正批评!

发布了17 篇原创文章 · 获赞 4 · 访问量 3397

猜你喜欢

转载自blog.csdn.net/u013553309/article/details/74011223
今日推荐