基于注解方式 springboot + mybatis + Druid多数据源(oracle+mysql)
基本架构简要原理:1. 创建用于数据源切换注解@DataBase2. 创建Aspect切面类DataSourceAspect,用于完成在实际操作前根据注解内容动态切换数据源动作3. DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法4. DynamicDataSource继承Abstra...
·
基本架构
简要原理:
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文件
在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
自定义注解DataBase
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";
}
定义切面类DataSourceAspect
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;
}
}
定义线程安全类DatabaseContextHolder
/**
* @ClassName DatabaseContextHolder
* @Description TODO(保证线程安全的DatabaseType容器)
* @author 寻找手艺人
* @Date 2019年5月5日 上午11:32:38
* @version 1.0.0
*/
public class DatabaseContextHolder {
private static final ThreadLocal<String> 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();
}
}
定义DynamicDataSource类
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();
}
}
创建配置数据源DruidConfig类
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<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseContextHolder.DEFAULT_DS, datasource1);
targetDataSources.put(DatabaseContextHolder.SECOND_DS, datasource2);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(datasource1);// 默认的datasource设置为dataSourceDB1
return dataSource;
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
// 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
// fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
// fb.setMapperLocations(
// new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//
return fb.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public Filter statFilter(){
StatFilter filter = new StatFilter();
filter.setSlowSqlMillis(5000);
filter.setLogSlowSql(true);
filter.setMergeSql(true);
return filter;
}
@Bean
public ServletRegistrationBean servletRegistrationBean(){
//org.springframework.boot.context.embedded.ServletRegistrationBean提供类的进行注册.
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
//添加初始化参数:initParams
//白名单:
// servletRegistrationBean.addInitParameter("allow","127.0.0.1");
//IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
//servletRegistrationBean.addInitParameter("deny","192.168.1.73");
//登录查看信息的账号密码.
servletRegistrationBean.addInitParameter("loginUsername","admin");
servletRegistrationBean.addInitParameter("loginPassword","123456");
//在日志中打印执行慢的sql语句
servletRegistrationBean.addInitParameter("logSlowSql", "true");
//是否能够重置数据.
servletRegistrationBean.addInitParameter("resetEnable","false");
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean druidStatFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
//过滤文件类型
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
//监控单个url调用的sql列表
filterRegistrationBean.addInitParameter("profileEnable", "true");
return filterRegistrationBean;
}
}
应用UserMapper类
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<User> getAllOrclUsers();
@Select("select userId as id,realName as name from sys_user")
public List<User> getAllMySQLUsers();
}
应用UserService类
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<List<User>> getAllOrclUsers();
public Result<List<User>> getAllMySQLUsers();
}
应用重点UserServiceImpl
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<List<User>> getAllOrclUsers() {
List<User> users = userMapper.getAllOrclUsers();
return new Result<List<User>>(ResultEnum.SUCCESS,users);
}
@Override
@DataBase("db2")
public Result<List<User>> getAllMySQLUsers() {
List<User> users = userMapper.getAllMySQLUsers();
return new Result<List<User>>(ResultEnum.SUCCESS,users);
}
}
测试
读取oracle
读取mysql
更多推荐
已为社区贡献2条内容
所有评论(0)