提供一个mybatisplus的mapper文件动态刷新配置类
方便开发时使用,不用每次修改xml文件后都要去重启应用


package com.xxx.config;import java.util.Arrays;
import java.util.List;
import java.util.Set;import org.apache.commons.collections4.IteratorUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.transaction.annotation.EnableTransactionManagement;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.google.common.collect.Sets;@Configuration
@EnableTransactionManagement
@MapperScan({ "com.xxx.*.dao", "com.xxx.dao" })
@ConditionalOnClass(PaginationInterceptor.class)
public class MyBatisPlusConfig {@Autowiredprivate MybatisPlusProperties mybatisPlusProperties;// 分页插件@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();// 设置最大单页限制数量,默认 500 条,-1 不受限制paginationInterceptor.setLimit(Integer.MAX_VALUE);// 开启 count 的 join 优化,只针对部分 left joinpaginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));// 设置方言paginationInterceptor.setDialectType(DbType.MYSQL.getDb());return paginationInterceptor;}/*** 自动刷新插件** @return*///@ConditionalOnProperty("mybatis-plus.global-config.refresh")@Beanpublic MybatisMapperRefresh mybatisMapperRefresh(ApplicationContext applicationContext,SqlSessionFactory sqlSessionFactory) {Set<Resource> mapperLocations = Sets.newLinkedHashSet();for (String xx : mybatisPlusProperties.getMapperLocations()) {try {mapperLocations.addAll(Arrays.asList(applicationContext.getResources(xx)));} catch (Exception e) {continue;}}List<Resource> list = IteratorUtils.toList(mapperLocations.iterator());Resource[] array = list.toArray(new Resource[list.size()]);MybatisMapperRefresh mybatisMapperRefresh = new MybatisMapperRefresh(array, sqlSessionFactory, 10, 5, true);return mybatisMapperRefresh;}}

package com.xxx.config;import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.util.ResourceUtils;import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.SystemClock;
import com.google.common.collect.Lists;/*** 切莫用于生产环境(后果自负),* <p>Mybatis 映射文件热加载(发生变动后自动重新加载).</p>* <p>方便开发时使用,不用每次修改xml文件后都要去重启应用.</p>*/
public class MybatisMapperRefresh implements Runnable {private static final Log logger = LogFactory.getLog(MybatisMapperRefresh.class);/*** 记录jar包存在的mapper*/private static final Map<String, List<Resource>> jarMapper = new HashMap<>();private SqlSessionFactory sqlSessionFactory;private Resource[] mapperLocations;private volatile Long beforeTime = 0L;private Configuration configuration;/*** 是否开启刷新mapper*/private boolean enabled;/*** xml文件目录*/private Set<String> fileSet;/*** 延迟加载时间*/private int delaySeconds = 10;/*** 刷新间隔时间*/private int sleepSeconds = 20;public MybatisMapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, int delaySeconds,int sleepSeconds, boolean enabled) {this.mapperLocations = mapperLocations.clone();this.sqlSessionFactory = sqlSessionFactory;this.delaySeconds = delaySeconds;this.enabled = enabled;this.sleepSeconds = sleepSeconds;this.configuration = sqlSessionFactory.getConfiguration();this.run();}public MybatisMapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, boolean enabled) {this.mapperLocations = mapperLocations.clone();this.sqlSessionFactory = sqlSessionFactory;this.enabled = enabled;this.configuration = sqlSessionFactory.getConfiguration();this.run();}@Overridepublic void run() {final GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);/** 启动 XML 热加载*/if (enabled) {beforeTime = SystemClock.now();final MybatisMapperRefresh runnable = this;new Thread(new Runnable() {@Overridepublic void run() {if (fileSet == null) {fileSet = new HashSet<>();if (mapperLocations != null) {for (Resource mapperLocation : mapperLocations) {try {if (ResourceUtils.isJarURL(mapperLocation.getURL())) {String key = new UrlResource(ResourceUtils.extractJarFileURL(mapperLocation.getURL())).getFile().getPath();fileSet.add(key);if (jarMapper.get(key) != null) {jarMapper.get(key).add(mapperLocation);} else {List<Resource> resourcesList = new ArrayList<>();resourcesList.add(mapperLocation);jarMapper.put(key, resourcesList);}} else {fileSet.add(mapperLocation.getFile().getPath());}} catch (IOException ioException) {ioException.printStackTrace();}}}}try {Thread.sleep(delaySeconds * 1000);} catch (InterruptedException interruptedException) {interruptedException.printStackTrace();}do {try {for (String filePath : fileSet) {File file = new File(filePath);if (file.isFile() && file.lastModified() > beforeTime) {// 记录上次重新加载时间防止重复加载已经重载的文件beforeTime = file.lastModified();List<Resource> removeList = jarMapper.get(filePath);if (removeList != null && !removeList.isEmpty()) {for (Resource resource : removeList) {runnable.refresh(resource);}} else {runnable.refresh(new FileSystemResource(file));}}}} catch (Exception exception) {exception.printStackTrace();}try {Thread.sleep(sleepSeconds * 1000);} catch (InterruptedException interruptedException) {interruptedException.printStackTrace();}} while (true);}}, "mybatis-plus MapperRefresh").start();}}/*** 刷新mapper** @throws Exception*/@SuppressWarnings("rawtypes")private void refresh(Resource resource)throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {this.configuration = sqlSessionFactory.getConfiguration();boolean isSupper = configuration.getClass().getSuperclass() == Configuration.class;try {Field loadedResourcesField = isSupper? configuration.getClass().getSuperclass().getDeclaredField("loadedResources"): configuration.getClass().getDeclaredField("loadedResources");loadedResourcesField.setAccessible(true);Set loadedResourcesSet = ((Set) loadedResourcesField.get(configuration));XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(),new XMLMapperEntityResolver());XNode context = xPathParser.evalNode("/mapper");String namespace = context.getStringAttribute("namespace");Field field = MapperRegistry.class.getDeclaredField("knownMappers");field.setAccessible(true);Map mapConfig = (Map) field.get(configuration.getMapperRegistry());Collection<String> mappedStatementNames = configuration.getMappedStatementNames();mapConfig.remove(Resources.classForName(namespace));loadedResourcesSet.remove(resource.toString());configuration.getCacheNames().remove(namespace);cleanParameterMap(context.evalNodes("/mapper/parameterMap"), namespace);cleanResultMap(context.evalNodes("/mapper/resultMap"), namespace);cleanKeyGenerators(context.evalNodes("insert|update|select|delete"), namespace);cleanSqlElement(context.evalNodes("/mapper/sql"), namespace);XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),sqlSessionFactory.getConfiguration(), resource.toString(),sqlSessionFactory.getConfiguration().getSqlFragments());xmlMapperBuilder.parse();logger.debug("refresh: '" + resource + "', success!");} catch (IOException e) {logger.error("Refresh IOException :" + e.getMessage());} finally {ErrorContext.instance().reset();}}/*** 清理parameterMap** @param list* @param namespace*/private void cleanParameterMap(List<XNode> list, String namespace) {for (XNode parameterMapNode : list) {String id = parameterMapNode.getStringAttribute("id");configuration.getParameterMaps().remove(namespace + "." + id);}}/*** 清理resultMap** @param list* @param namespace*/private void cleanResultMap(List<XNode> list, String namespace) {for (XNode resultMapNode : list) {String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());configuration.getResultMapNames().remove(id);configuration.getResultMapNames().remove(namespace + "." + id);clearResultMap(resultMapNode, namespace);}}private void clearResultMap(XNode xNode, String namespace) {for (XNode resultChild : xNode.getChildren()) {if ("association".equals(resultChild.getName()) || "collection".equals(resultChild.getName())|| "case".equals(resultChild.getName())) {if (resultChild.getStringAttribute("select") == null) {configuration.getResultMapNames().remove(resultChild.getStringAttribute("id", resultChild.getValueBasedIdentifier()));configuration.getResultMapNames().remove(namespace + "."+ resultChild.getStringAttribute("id", resultChild.getValueBasedIdentifier()));if (resultChild.getChildren() != null && !resultChild.getChildren().isEmpty()) {clearResultMap(resultChild, namespace);}}}}}/*** 清理selectKey** @param list* @param namespace*/private void cleanKeyGenerators(List<XNode> list, String namespace) {for (XNode context : list) {String id = context.getStringAttribute("id");configuration.getKeyGeneratorNames().remove(id + SelectKeyGenerator.SELECT_KEY_SUFFIX);configuration.getKeyGeneratorNames().remove(namespace + "." + id + SelectKeyGenerator.SELECT_KEY_SUFFIX);Collection<MappedStatement> mappedStatements = configuration.getMappedStatements();List<MappedStatement> objects = Lists.newArrayList();Iterator<MappedStatement> it = mappedStatements.iterator();while (it.hasNext()) {Object object = it.next();if (object instanceof org.apache.ibatis.mapping.MappedStatement) {MappedStatement mappedStatement = (MappedStatement) object;if (mappedStatement.getId().equals(namespace + "." + id)) {objects.add(mappedStatement);}}}mappedStatements.removeAll(objects);}}/*** 清理sql节点缓存** @param list* @param namespace*/private void cleanSqlElement(List<XNode> list, String namespace) {for (XNode context : list) {String id = context.getStringAttribute("id");configuration.getSqlFragments().remove(id);configuration.getSqlFragments().remove(namespace + "." + id);}}
}

最后配置文件配置


mybatis-plus.global-config.refresh = true

动态刷新mapper看过来相关推荐

  1. mysql-plus多数据库_IDEA项目搭建九——MybatisPlus多数据库实现

    一.简介 MybatisPlus中引用多数据库时,传统的配置就失效了,需要单独写配置来实现,下面就说一下具体应该如何操作 二.引入MybatisPlus多数据源配置 还是先看一下我的项目结构,Mode ...

  2. spring整合mybatis接口无法注入问题

    在学习Spring完之后简单的了解了MyBatis.然后进行简单的整合,遇到MyBatista接口映射的Bean无法自动注入的问题: 代码异常: 线程"main"org.sprin ...

  3. java流式传输对象_使用Java 8在地图上流式传输

    java流式传输对象 在本文中,我将向您展示如何在标准Java映射上有效地实现Speedment Open Source流,并将Stream接口扩展为MapStream! 即使在复杂的情况下,此添加将 ...

  4. 使用Java 8在地图上流式传输

    在本文中,我将向您展示如何在标准Java映射上有效地实现Speedment Open Source流,并将Stream接口扩展为MapStream! 即使在复杂的情况下,此添加将使保持流的具体性和可读 ...

  5. mybatis --入门 单表增删改查-curd

    目录 1. mybatis 环境搭建 2. 实体类映射文件配置(写sql) 3. mybatis核心配置文件 (环境配置) 4. 测试 mybatis document https://mybatis ...

  6. 关于Mybatis的几个问题

    本文来说下关于Mybatis的几个问题 文章目录 概述 持久层的那些事 什么是 JDBC JDBC 原理 什么是 Mybatis Mybatis 与 JDBC 的关系 Mybatis 关键词说明 My ...

  7. 大数据_Hbase-API访问_Java操作Hbase_MR-数据迁移-代码测试---Hbase工作笔记0017

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 然后我们继续写,我们要通过mapper,把数据从一个表中查询出来,然后,再把数据, 弄成put,然 ...

  8. Mybatis源码学习-动态代理

    Mybatis源码学习-动态代理 binding包下面是mybatis的mapper动态代理 // Mybatis官方手册建议通过mapper对象访问mybatis,因为使用mapper看起来更优雅 ...

  9. mybatis从零基础到增删改查数据库

    本文是mybatis框架一个初步的入门总结,最全的最好的资料应该参考这个:http://mybatis.github.io/mybatis-3/zh/index.html 本文在Eclipse下搭建一 ...

最新文章

  1. 重走丝绸之路:海尔如何探索全球生活智慧?
  2. WTL 出现的SetMsgHandled和IsMsgHandled 错误
  3. vim7.4官方源码在vs2013的编译方法及问题总结
  4. JAVAWEB技术之七过滤器
  5. Linux下PortSentry的配置
  6. tensorflow训练神经网络时loss出现nan的问题
  7. python 加密方法总结
  8. pwm调速流程图小车_PWM调速+循迹__智能小车程序
  9. 【渝粤教育】国家开放大学2018年春季 8643-22T数据库基础与应用 参考试题
  10. IPC$局域网入侵详解
  11. 海归首选“北上广” 薪资期望不太高 元芳你怎么看?
  12. 请别再使用 SimpleDateFormat 格式化时间了,DateTimeFormatter 更出色!
  13. VScode如何自动换行
  14. Python给Word加水印
  15. 2017年12月。。
  16. 最适合0基础入门的5种编程语言
  17. div从上到下从左到右自动换行显示排列
  18. php异步检测用户名是否存在,AJAX_Ajax——异步检查用户名是否存在示例,在任何网站注册用户的时候, - phpStudy...
  19. linux桌面版和服务器版区别_简单普及一些linux和windows的区别,减少新入手一些云服务时的不适应...
  20. 微信小程序自定义车牌号输入键盘-附源码

热门文章

  1. 什么是GNSS模拟器及其应用?
  2. Altera FPGA 差分信号初识(2)
  3. short—溢出问题
  4. C/C++编程打造单机麻将「附源码+说明文档」一个不错的入门项目
  5. dedecms 只需要几个步骤快速实现独立的手机版功能
  6. 诶,突然又想玩梦幻了~~~~~~
  7. 树莓派教程(4)——树莓派配置远程桌面
  8. Elastic_Stack
  9. 期货程序化反向跟单·第二章
  10. 数据仓库技术的发展历程