多数据源切换工具——基于springAOP、注解

多数据源切换工具——基于springAOP、注解

Scroll Down

多数据源切换工具——基于springAOP、注解

项目涉及到多数据源的切换,需要根据对应业务切换到对应数据库的schemal下面进行查找,所以进行数据切换工具的设计。

1.思路

1.基本工具的封装

1.数据库枚举(匹配我们对应的数据库)


public enum DbDialectEnum
{
  DB2("db2", "db2数据库"), 
  SQLSERVER("sqlserver", "sqlserver数据库"), 
  ORACLE("oracle", "oracle数据库"), 
  MYSQL("mysql", "mysql数据库"), 
  POSTGRESQL("postgresql", "postgresql数据库");

  private String desc;
  private String code;

  private DbDialectEnum(String code, String desc) { this.code = code;
    this.desc = desc;
  }

  public String getCode()
  {
    return this.code;
  }

  public void setCode(String code)
  {
    this.code = code;
  }

  public String getDesc()
  {
    return this.desc;
  }

  public void setDesc(String desc)
  {
    this.desc = desc;
  }

  public boolean equals(String code) {
    return this.code.equals(code);
  }
}

2.上下文工具类(帮助拿到bean)

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextHelper
  implements ApplicationContextAware
{
  private static ApplicationContext applicationContext;

  public void setApplicationContext(ApplicationContext applicationContext)
    throws BeansException
  {
    applicationContext = applicationContext;
  }

  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }

  public static <T> T getBean(String name) throws BeansException
  {
    return applicationContext.getBean(name);
  }
  public static <T> T getBean(Class<T> t) throws BeansException {
    return applicationContext.getBean(t);
  }
}

3.数据库的载体类


import java.sql.Connection;

import java.sql.DatabaseMetaData;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.DataSourceUtils;

import org.springframework.util.StringUtils;

public class DBContextHolder

{

  private static final ThreadLocal<String> contextHolder = new ThreadLocal();



  private static Map<String, String> dialetMap = new HashMap();



  public static void setDataSource(String dataSource) {

    contextHolder.set(dataSource);

  }



  public static void setDefaultSource()

  {

    contextHolder.set(getDataSourceDefaultId());

  }



  public static String getDataSource()

  {

    return (String)contextHolder.get();

  }



  public static void clearDataSource() {

    contextHolder.remove();

  }



  public static String getDataSourceDefaultId()

  {

    String dataSourceDefaultId = "";

    DynamicDataSource dynamicDataSource = 

      (DynamicDataSource)SpringContextHelper.getBean("multiDataSource");

    Map dynamicTargetDataSources = dynamicDataSource

      .getDynamicTargetDataSources();

    Iterator it = dynamicTargetDataSources.entrySet().iterator();

    while (it.hasNext()) {

      Map.Entry entry = (Map.Entry)it.next();

      Object key = entry.getKey();

      Object value = entry.getValue();

      if (value == dynamicDataSource.getDynamicDefaultTargetDataSource()) {

        dataSourceDefaultId = String.valueOf(key);

      }

    }

    return dataSourceDefaultId;

  }



  public static List<String> getDataSourceIdList()

  {

    List dataSourceIdList = new ArrayList();

    DynamicDataSource dynamicDataSource = 

      (DynamicDataSource)SpringContextHelper.getBean("multiDataSource");

    Map dynamicTargetDataSources = dynamicDataSource

      .getDynamicTargetDataSources();

    Iterator it = dynamicTargetDataSources.entrySet().iterator();

    while (it.hasNext()) {

      Map.Entry entry = (Map.Entry)it.next();

      Object key = entry.getKey();

      Object value = entry.getValue();

      dataSourceIdList.add(String.valueOf(key));

    }

    return dataSourceIdList;

  }



  public static String getDialet()

  {

    Connection conn = null;

    String value = "";



    String datasourceName = getDataSource();

    if (StringUtils.isEmpty(datasourceName)) {

      datasourceName = "default";

    }



    if (dialetMap.get(datasourceName) != null) {

      return (String)dialetMap.get(datasourceName);

    }

    try

    {

      DynamicDataSource dynamicDataSource = (DynamicDataSource)SpringContextHelper.getBean("multiDataSource");

      Map dynamicTargetDataSources = dynamicDataSource.getDynamicTargetDataSources();

      DataSource datasource = null;

      if ("default".equals(datasourceName))

        datasource = (DataSource)dynamicDataSource.getDynamicDefaultTargetDataSource();

      else if (dynamicTargetDataSources.containsKey(datasourceName)) {

        datasource = (DataSource)dynamicTargetDataSources.get(datasourceName);

      }

      conn = DataSourceUtils.getConnection(datasource);



      value = getDialect(conn);



      dialetMap.put(datasourceName, value);

    } catch (Exception e) {

      e.printStackTrace();

      try

      {

        if ((conn != null) && (!conn.isClosed()))

          conn.close();

      }

      catch (SQLException e) {

        e.printStackTrace();

      }

    }

    finally

    {

      try

      {

        if ((conn != null) && (!conn.isClosed()))

          conn.close();

      }

      catch (SQLException e) {

        e.printStackTrace();

      }

    }

    return value;

  }



  public static String getDialect(Connection conn)

  {

    String dialetString = null;

    String value = null;

    try {

      dialetString = conn.getMetaData().getDatabaseProductName();

      if (dialetString.contains("DB2"))

        value = DbDialectEnum.DB2.getCode();

      else if (dialetString.contains("SQL Server"))

        value = DbDialectEnum.SQLSERVER.getCode();

      else if (dialetString.contains("Oracle"))

        value = DbDialectEnum.ORACLE.getCode();

      else if (dialetString.contains("MySQL"))

        value = DbDialectEnum.MYSQL.getCode();

      else if (dialetString.contains("DM DBMS"))

        value = DbDialectEnum.ORACLE.getCode();

      else if (dialetString.contains("PostgreSQL"))

        value = DbDialectEnum.POSTGRESQL.getCode();

    }

    catch (SQLException e) {

      e.printStackTrace();

    }

    return value;

  }

}

多数据源类:


import java.io.PrintStream;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource
{
  PageHelperDialect pageHelperDialect = new PageHelperDialect();

  private boolean debug = false;
  private Map<Object, Object> dynamicTargetDataSources;
  private Object dynamicDefaultTargetDataSource;

  protected Object determineCurrentLookupKey()
  {
    String datasource = DBContextHolder.getDataSource();

    if (this.debug)
    {
      if (StringUtils.isEmpty(datasource))
        System.out.println("---当前数据源:默认数据源---");
      else {
        System.out.println("---当前数据源:" + datasource + "---");
      }
    }

    return datasource;
  }

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

  public void setDefaultTargetDataSource(Object defaultTargetDataSource)
  {
    super.setDefaultTargetDataSource(defaultTargetDataSource);
    this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
  }

  public void setDebug(boolean debug)
  {
    this.debug = debug;
  }

  public boolean isDebug()
  {
    return this.debug;
  }

  public Map<Object, Object> getDynamicTargetDataSources()
  {
    return this.dynamicTargetDataSources;
  }

  public void setDynamicTargetDataSources(Map<Object, Object> dynamicTargetDataSources)
  {
    this.dynamicTargetDataSources = dynamicTargetDataSources;
  }

  public Object getDynamicDefaultTargetDataSource()
  {
    return this.dynamicDefaultTargetDataSource;
  }

  public void setDynamicDefaultTargetDataSource(Object dynamicDefaultTargetDataSource)
  {
    this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
  }
}

3.切面实现多数据源切换

1.定义数据源名称(与配置对应名称一致)


public final class DataSourceContextHolder {
	
	public static final String DEFAULT_DATA_SOURCE = "datasource1";
	
	public static final String DATA_SOURCE_B = "datasource2";
	
	public static final String DATA_SOURCE_C = "datasource3";

}

2.注解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface DataSourceAnnotation {

    String value() default DataSourceContextHolder.DEFAULT_DATA_SOURCE;

}

3.切面(需要在对应位置换上包的名字)


import java.lang.reflect.Method;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;




@Aspect
@Component
@Order(1)
public class DataSourceAspect {

	private static final Logger LOGGER = Logger.getLogger(DataSourceAspect.class);

    @Before("execution(* xx.xx.xx..*.*Service.*(..))")
    public void before(JoinPoint point) {
    	Class<?> className = point.getTarget().getClass();
    	
        String targetDataSource = null;
        
        DataSourceAnnotation typeAnnotation = className.getAnnotation(DataSourceAnnotation.class);
        if (typeAnnotation != null) {
        	targetDataSource = typeAnnotation.value();
		}
        
        Signature signature = point.getSignature();
		try {
			Method method = className.getMethod(signature.getName(), ((MethodSignature) signature).getParameterTypes());
			DataSourceAnnotation methodAnnotation = method.getAnnotation(DataSourceAnnotation.class);
			if (methodAnnotation != null) {
				targetDataSource = methodAnnotation.value();
			}
			
			if (StringUtils.isNotBlank(targetDataSource)) {
				DBContextHolder.setDataSource(targetDataSource);
				LOGGER.info("数据源切换为 ========> " + targetDataSource);
			}
		} catch (Exception e) {
			LOGGER.error(e);
		}
    }

	@After("execution(* xx.xx.xx..*.*Service.*(..))")
    public void after(JoinPoint point) {
		if (DBContextHolder.getDataSource() != null) {
			DBContextHolder.clearDataSource();
			LOGGER.info("<======== 清除数据源 ========> ");
		}
    }
	
}

4.配置

只需在DB的配置DynamicDataSource的bean将对应数据源配置进来即可

5.总结

首先用本地线程来装我们对应多数据库的连接,然后将本地线程交给DynamicDataSource,然后利用切面的形式在需要的service方法打上注解配置对应数据源名称就行了