1. 创建用于数据源切换注解@DataBase
2. 创建Aspect切面类DataSourceAspect,用于完成在实际操作前根据注解内容动态切换数据源动作
3. DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
4. DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用DatabaseContextHolder获取当前线程的DatabaseType
5. DruidConfig中生成2个数据源DataSource的bean---value
6. DruidConfig中将组成的key-value对写入到DynamicDataSource动态数据源的targetDataSources属性(同时也会设置2个数据源其中的一个为DynamicDataSource的defaultTargetDataSource属性中)
7. 将DynamicDataSource数据源注入到SqlSessionFactory的dataSource属性中去,并且该dataSource作为transactionManager的入参来构造DataSourceTransactionManager
8. 在serviceImpl层,在具体方法上加入@DataBase("dbType")注解
注意:在进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源:先根据设置去targetDataSources中去找,若没有,则选择defaultTargetDataSource),之后在进行数据库操作。
在application.properties配置文件配置连个数据源。一个mysql一个oracle
# mysql
spring.datasource.db2.driverClassName=com.mysql.jdbc.Driver
spring.datasource.db2.url=jdbc:mysql://192.168.0.189:3306/demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.db2.username=root
spring.datasource.db2.password=898
# oracle
spring.datasource.db1.driverClassName: oracle.jdbc.driver.OracleDriver
spring.datasource.db1.url: jdbc:oracle:thin:@192.168.20.17:1521:ORCL
spring.datasource.db1.username: orcl
spring.datasource.db1.password: orcl
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName DataBase
* @Description TODO(注解)
* @author 寻找手艺人
* @Date 2019年5月6日 上午9:15:01
* @version 1.0.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataBase {
String value() default "db1";
}
import java.lang.reflect.Method;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import edu.jliae.card.common.DatabaseContextHolder;
/**
* @ClassName DataSourceAspect
* @Description TODO(切面类)
* @author 寻找手艺人
* @Date 2019年5月6日 上午9:10:36
* @version 1.0.0
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered{
@Pointcut("@annotation(edu.jliae.aspect.DataBase)")//注意:这里的xxxx代表的是上面public @interface DataSource这个注解DataSource的包名
public void dataSourcePointCut() {
}
@SuppressWarnings("rawtypes")
@Before("dataSourcePointCut()")
public void beforeSwitchDS(JoinPoint point){
//获得当前访问的class
Class> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DatabaseContextHolder.DEFAULT_DS;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DateBase注解
if (method.isAnnotationPresent(DataBase.class)) {
DataBase annotation = method.getAnnotation(DataBase.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DatabaseContextHolder.setDatabaseType(dataSource);
}
@After("dataSourcePointCut()")
public void afterSwitchDS(JoinPoint point){
DatabaseContextHolder.clearDataSource();
}
@Override
public int getOrder() {
return 1;
}
}
/**
* @ClassName DatabaseContextHolder
* @Description TODO(保证线程安全的DatabaseType容器)
* @author 寻找手艺人
* @Date 2019年5月5日 上午11:32:38
* @version 1.0.0
*/
public class DatabaseContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
//默认数据源
public static final String DEFAULT_DS = "db1";
public static final String SECOND_DS = "db2";
public static void setDatabaseType(String type) {
contextHolder.set(type);
}
public static String getDatabaseType() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @ClassName DynamicDataSource
* @Description TODO(重新determineCurrentLookupKey获取方式方法)
* @author寻找手艺人
* @Date 2019年5月5日 上午11:33:25
* @version 1.0.0
*/
public class DynamicDataSource extends AbstractRoutingDataSource{
/**
* 动态数据源(需要继承AbstractRoutingDataSource)
*/
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.google.common.collect.Lists;
import edu.jliae.card.common.DatabaseContextHolder;
import edu.jliae.card.common.DynamicDataSource;
/**
* @ClassName DruidConfig
* @Description TODO(druid监控配置)
* @author 寻找手艺人
* @Date 2019年4月23日 上午11:55:24
* @version 1.0.0
*/
@Configuration
public class DruidConfig {
@Autowired
private Environment env;
@Primary
@ConfigurationProperties(prefix="spring.datasource.db1")
@Bean(name = "datasource1")
public DataSource dataSourceDB1(Filter statFilter) throws SQLException{
DruidDataSource dataSource = new DruidDataSource();
dataSource.setProxyFilters(Lists.newArrayList(statFilter()));
return dataSource;
}
@ConfigurationProperties(prefix="spring.datasource.db2")
@Bean(name = "datasource2")
public DataSource dataSourceDB2(Filter statFilter) throws SQLException{
DruidDataSource dataSource = new DruidDataSource();
dataSource.setProxyFilters(Lists.newArrayList(statFilter()));
return dataSource;
}
/**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean(name = "dynamicDataSource")
public DynamicDataSource dataSource(@Qualifier("datasource1") DataSource datasource1,
@Qualifier("datasource2") DataSource datasource2) {
Map
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import edu.jliae.card.entry.User;
/**
* @ClassName UserMapper
* @Description TODO(这里用一句话描述这个类的作用)
* @author 寻找手艺人
* @Date 2019年4月23日 上午11:09:04
* @version 1.0.0
*/
@Mapper
public interface UserMapper {
@Select("select id as id,name as name from m_base_dic")
public List getAllOrclUsers();
@Select("select userId as id,realName as name from sys_user")
public List getAllMySQLUsers();
}
import java.util.List;
import edu.jliae.card.common.Result;
import edu.jliae.card.entry.User;
/**
* @ClassName UserService
* @Description TODO(这里用一句话描述这个类的作用)
* @author 寻找手艺人
* @Date 2019年4月23日 上午11:22:13
* @version 1.0.0
*/
public interface UserService {
public Result> getAllOrclUsers();
public Result> getAllMySQLUsers();
}
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import edu.jliae.aspect.DataBase;
import edu.jliae.card.common.Result;
import edu.jliae.card.common.ResultEnum;
import edu.jliae.card.entry.User;
import edu.jliae.card.mapper.UserMapper;
import edu.jliae.card.service.UserService;
/**
* @ClassName UserServiceImpl
* @Description TODO(这里用一句话描述这个类的作用)
* @author 寻找手艺人
* @Date 2019年4月23日 上午11:22:56
* @version 1.0.0
*/
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
@DataBase("db1")
public Result> getAllOrclUsers() {
List users = userMapper.getAllOrclUsers();
return new Result>(ResultEnum.SUCCESS,users);
}
@Override
@DataBase("db2")
public Result> getAllMySQLUsers() {
List users = userMapper.getAllMySQLUsers();
return new Result>(ResultEnum.SUCCESS,users);
}
}