公司最近要我写一个动态数据源的切换,使得用户能在前台选择一些库动态的进行切换,查询不同库不同模块下的数据信息,数据源连接库的信息都在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,一般都在线!有说的不对的地方,欢迎大家指正批评!