MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。
下面小编就为大家带来一篇 spring boot+mybatis 多数据源切换 (实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
由于公司业务划分了多个数据库,开发一个项目会同事调用多个库,经过学习我们采用了注解 + aop 的方式实现的
1. 首先定义一个注解类
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface TargetDataSource {
- String value();//此处接收的是数据源的名称
- }
2. 然后建一个配置类,这个在项目启动时会加载数据源,一开始采用了 HikariCP,查资料说是最快性能最好的,然后又发现了阿里的 druid,这个功能比较全面,而且性能也还可以,最主要他还有监控功能,具体实现看如下代码
- package com.example.demo.datasource;
- import com.alibaba.druid.pool.DruidDataSource;
- import com.alibaba.druid.support.http.StatViewServlet;
- import com.alibaba.druid.support.http.webStatFilter;
- import com.example.demo.datasource.DynamicDataSource;
- import com.zaxxer.hikari.HikariConfig;
- import com.zaxxer.hikari.HikariDataSource;
- import lombok.extern.slf4j.Slf4j;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.beans.factory.annotation.Value;
- 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.jdbc.datasource.DataSourceTransactionManager;
- import org.springframework.scheduling.annotation.EnableScheduling;
- import org.springframework.scheduling.annotation.Scheduled;
- import org.springframework.transaction.PlatformTransactionManager;
- import org.w3c.dom.NodeList;
- import org.w3c.dom.Document;
- import org.w3c.dom.Element;
- import org.w3c.dom.Node;
- import javax.servlet.annotation.WebInitParam;
- import javax.servlet.annotation.WebServlet;
- import javax.sql.DataSource;
- import javax.xml.parsers.DocumentBuilder;
- import javax.xml.parsers.DocumentBuilderFactory;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.util.HashMap;
- import java.util.Map;
- import java.io.File;
- import com.alibaba.druid.support.http.StatViewServlet;
- /**
- * Author: wangchao
- * Version:
- * Date: 2017/9/11
- * Description:数据源配置
- * Modification History:
- * Date Author Version Description
- * --------------------------------------------------------------
- * Why & What is modified:
- */
- @Configuration@EnableScheduling public class DataSourceConfig {
- /*@Autowired
- private DBProperties properties;*/
- @Value("${datasource.filePath}") private String filePath; //数据源配置
- @Bean(name = "dataSource") public DataSource dataSource() {
- //按照目标数据源名称和目标数据源对象的映射存放在Map中
- Map < Object,
- Object > targetDataSources = new HashMap < >();
- //查找xml数据连接字符串
- targetDataSources = getdataMap(filePath);
- //动态获取DBProperties类申明的属性
- /*Field[] fields=properties.getClass().getDeclaredFields();
- for(int i=0;i<fields.length;i++)
- {
- targetDataSources.put(fields[i].getName(), getFieldValueByName(fields[i].getName(),properties));
- }*/
- //采用是想AbstractRoutingDataSource的对象包装多数据源
- DynamicDataSource dataSource = new DynamicDataSource();
- dataSource.setTargetDataSources(targetDataSources);
- //设置默认的数据源,当拿不到数据源时,使用此配置
- //dataSource.setDefaultTargetDataSource(properties.getUzaiTravel());
- return dataSource;
- }
- @Bean public PlatformTransactionManager txManager() {
- return new DataSourceTransactionManager(dataSource());
- }
- /**
- *获取数据源集合
- */
- private Map < Object,
- Object > getdataMap(String fiePath) {
- try {
- Map < Object,
- Object > targetDataSources = new HashMap < >();
- File xmlFile = new File(fiePath);
- DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder builder = builderFactory.newDocumentBuilder();
- Document doc = builder.parse(xmlFile);
- doc.getDocumentElement().normalize();
- System.out.println("Root element: " + doc.getDocumentElement().getNodeName());
- NodeList nList = doc.getElementsByTagName("db");
- for (int i = 0; i < nList.getLength(); i++) {
- Node node = nList.item(i);
- Element ele = (Element) node;
- /*HikariConfig config = new HikariConfig();
- config.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());
- config.setJdbcUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());
- config.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());
- config.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());
- //config.addDataSourceProperty("password", ele.getElementsByTagName("password").item(0).getTextContent());
- HikariDataSource dataSource = new HikariDataSource(config);*/
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());
- dataSource.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());
- dataSource.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());
- dataSource.setUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());
- dataSource.setInitialSize(5);
- dataSource.setMinIdle(1);
- dataSource.setMaxActive(10); // 启用监控统计功能
- dataSource.setFilters("stat"); //设置是否显示sql语句
- targetDataSources.put(ele.getElementsByTagName("databasename").item(0).getTextContent(), dataSource);
- }
- return targetDataSources;
- } catch(Exception ex) {
- return null;
- }
- }
- //访问的ip
- @Value("${druid.IP}") private String IP;
- //登录名
- @Value("${druid.druidLgoinName}") private String druidLgoinName;
- //密码
- @Value("${druid.druidLgoinPassword}") private String druidLgoinPassword;
- @Bean public ServletRegistrationBean DruidStatViewServle() {
- //org.springframework.boot.context.embedded.ServletRegistrationBean提供类的进行注册.
- ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
- //添加初始化参数:initParams
- //白名单:
- servletRegistrationBean.addInitParameter("allow", IP);
- //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
- // servletRegistrationBean.addInitParameter("deny", "192.168.1.73");
- //登录查看信息的账号密码.
- servletRegistrationBean.addInitParameter("loginUsername", druidLgoinName);
- servletRegistrationBean.addInitParameter("loginPassword", druidLgoinPassword);
- //是否能够重置数据.
- servletRegistrationBean.addInitParameter("resetEnable", "false");
- return servletRegistrationBean;
- }
- /**
- * 注册一个:filterRegistrationBean
- * @return
- */
- @Bean public FilterRegistrationBean druidStatFilter2() {
- FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
- //添加过滤规则.
- filterRegistrationBean.addUrlPatterns("/*");
- //添加不需要忽略的格式信息.
- filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.CSS,*.ico,/druid/*");
- return filterRegistrationBean;
- }
- }
3. 动态数据源,从之前已加载的数据源中选取,DynamicDataSource 和 DynamicDataSourceHolder 配合使用
- public class DynamicDataSource extends AbstractRoutingDataSource {
- //数据源路由,此方用于产生要选取的数据源逻辑名称
- @Override protected Object determineCurrentLookupKey() {
- //从共享线程中获取数据源名称
- return DynamicDataSourceHolder.getDataSource();
- }
- }
- public class DynamicDataSourceHolder {
- /**
- * 本地线程共享对象
- */
- private static final ThreadLocal < String > THREAD_LOCAL = new ThreadLocal < >();
- public static void putDataSource(String name) {
- THREAD_LOCAL.set(name);
- }
- public static String getDataSource() {
- return THREAD_LOCAL.get();
- }
- public static void removeDataSource() {
- THREAD_LOCAL.remove();
- }
- }
4. 就是使用 aop,在 dao 层切换数据源
- @Component
- @Aspect
- public class DataSourceAspect {
- //切换放在mapper接口的方法上,所以这里要配置AOP切面的切入点
- @Pointcut("execution( * com.example.demo.dao.*.*(..))")
- public void dataSourcePointCut() {
- }
- @Before("dataSourcePointCut()")
- public void before(JoinPoint joinPoint) {
- Object target = joinPoint.getTarget();
- String method = joinPoint.getSignature().getName();
- Class<?>[] clazz = target.getClass().getInterfaces();
- Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
- try {
- Method m = clazz[0].getMethod(method, parameterTypes);
- //如果方法上存在切换数据源的注解,则根据注解内容进行数据源切换
- if (m != null && m.isAnnotationPresent(TargetDataSource.class)) {
- TargetDataSource data = m.getAnnotation(TargetDataSource.class);
- String dataSourceName = data.value();
- DynamicDataSourceHolder.putDataSource(dataSourceName);
- } else {
- }
- } catch (Exception e) {
- }
- }
- //执行完切面后,将线程共享中的数据源名称清空
- @After("dataSourcePointCut()")
- public void after(JoinPoint joinPoint){
- DynamicDataSourceHolder.removeDataSource();
- }
- }
数据连接都配置在 xml 里面
xml 路径在配置文件里面配置,这样适用读写分离和多个不同的数据源,而且多个项目可以共用这一个配置
最后引用注解,需要注意的是注解的数据库名称和 xml 里面 databasename 节点是一一对应的,可以随便自定义,比如读写是一个数据库名字,这时候就可以定义成 pringtest_r 表示读库
至此多数据源就配置完成,至于阿里的 druid 下次再分享,代码都贴出来,如果大家感觉还有哪些不足的地方,欢迎指正。
以上这篇 spring boot+mybatis 多数据源切换 (实例讲解) 就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持 PHPERZ。
来源: http://www.phperz.com/article/17/1212/353519.html