IDEA的热部署【MyBatis XML热部署 】

本文适用修改JAVA代码热部署、MyBatis XML的热部署

一、JAVA代码热部署.

 新版IDEA中:开启允许在运行过程中修改文件

  

最后要在Debug模式启动,可以看到热部署的加载文件了,可以手动点左边那个图标立即加载生效.

 二、MyBatis XML修改热部署.

MybatisMapperRefresh (mybatis-plus 2.0.7 API)

MyBatis在2.x的版本中移除了热加载的插件,我们实现的思路是手动加入这个MyBatis刷新的线程,本文以微服务框架SpringBoot为例.

引入依赖,注意和SpringBoot的版本适配。

        <!-- mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
MybatisMapperRefresh
package com.boot.skywalk.config;

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;
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 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;

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();
    }

    @Override
    public void run() {
        final GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
        /*
         * 启动 XML 热加载
         */
        if (enabled) {
            beforeTime = SystemClock.now();
            final MybatisMapperRefresh runnable = this;
            new Thread(new Runnable() {

                @Override
                public 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);
        }
    }
}
MybatisPlusConfig:在这里面修改多少秒加载参数和开关可配置化,注意生产环境严禁使用!!!
package com.boot.skywalk.config;

import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;

@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {

    @Autowired
    private MybatisPlusProperties mybatisPlusProperties;

    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    @Bean
    public MybatisMapperRefresh mybatisMapperRefresh(ApplicationContext applicationContext,
                                                     SqlSessionFactory sqlSessionFactory) {
        LinkedHashSet<Resource> mapperLocations = new LinkedHashSet<>();
        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;
    }
}

验证:修改Mapper中的SQL,过配置的时间就生效了。大型项目多模块开发环境比较实用. 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/5436.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MFCC语音特征值提取算法

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

银行数字化转型导师坚鹏:银行产品经理技能快速提升之道

银行产品经理技能快速提升之道 ——以推动银行战略目标实现为核心&#xff0c;实现知行果合一课程背景&#xff1a; 很多银行都在开展产品经理技能提升工作&#xff0c;目前存在以下问题急需解决&#xff1a; 不知道银行产品经理掌握哪些关键知识&#xff1f; 不清楚如何有效…

C++修炼之练气期第二层——缺省参数

目录 1.缺省参数的概念 2.缺省参数的分类 全缺省参数 半缺省参数 实用场景示例 1.缺省参数的概念 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。 在调用该函数时&#xff0c;如果没有指定实参则采用该形参的缺省值&#xff0c;否则使用指定的实参。 #inclu…

四大排序算法之归并排序

说明 为了自己学习方便&#xff0c;我这里总结了四大排序算法涵盖了七种排序算法 分类算法名称时间复杂度 空间复杂度稳定性插入排序 直接插入排序 希尔排序 O(n^2) O(1) O(n^2/3) O(1) 稳定 不稳定 选择排序 选择排序 堆排序 O(n^2) O(1) O(nlogn) O(1) 不稳…

linux查看进程、端口

1、先查看进程pidps -ef | grep 进程名如果已知pid&#xff0c;想看详情&#xff0c;则用 ps -ef pid2、通过pid查看占用端口(mac)netstat -na | grep 端口netstat -nap tcp | grep 进程pidnetstat -nap udp | grep 进程pid不加tcp或者udp的话mac上会报错&#xff1a;netstat常…

基于ASP的反垃圾邮件管理系统的设计与实现

随着Internet的迅速普及&#xff0c;电子邮件以其快捷、方便、低成本的特点逐渐成为人们进行信息交流的主要媒介之一&#xff0c;但是随之而来的垃圾邮件也越来越泛滥。垃圾邮件占用了有限的存储、计算和网络资源&#xff0c;耗费了用户大量的处理时间&#xff0c;影响和干扰了…

程序员OKR学习法

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl OKR管理法 OKR&#xff08;Objectives and Key Results&#xff09;管理法是一种目标管理方法&#xff0c;旨在通过制定明确的目标和可量化的关键结果来帮助组织、团队和个人…

RocketMQ的架构图

文章目录RocketMQ 技术架构中有四大角色 NameServer 、Broker 、Producer 、Consumer 。我来向大家分别解释一下这四个角色是干啥的。 Broker&#xff1a; 主要负责消息的存储、投递和查询以及服务高可用保证。说白了就是消息队列服务器嘛&#xff0c;生产者生产消息到 Broker…

Hive实战 --- 电子商务消费行为分析

目录 数据结构 Customer表 Transaction表 Store表 Review表 上传数据 创建目录用于存放数据 把本地文件上传到HDFS上 创建外部表 创建数据库 创建表 数据清洗 对transaction_details中的重复数据生成新ID 过滤掉store_review中没有评分的数据 找出PII (personal …

【web前端初级课程】第八章 什么是事件?

目录 一、事件情况汇总 二、标签绑定 三、使用DOM0事件模型 四、使用DOM2事件模型 五、相关练习&#xff1a;图片切换 一、事件情况汇总 事件分为三部分&#xff1a;事件源&#xff1a;绑定事件的标签、事件对象&#xff1a;就是事件产生的相关数据、事件处理函数 二、标…

Java使用功能方法交换a,b的值,通过构造方法输出姓名、年龄、家庭地址

目录 前言 一、使用功能方法交换a&#xff0c;b的值 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 二、通过构造方法输出姓名、年龄、家庭地址 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 前言 1.因多重原因&#xff0c;所以我…

愚人节,聊聊那些正在坑人的“新型AI”

几年前的一个愚人节&#xff0c;我们和大家聊过AI技术被作为诈骗工具的情况。很不幸&#xff0c;当时讨论的一些苗头&#xff0c;现在都成了电诈犯罪中屡见不鲜的手段。更可气的是&#xff0c;随着AI技术与应用本身的发展&#xff0c;犯罪分子的AI手段不减反增。一些“新型AI”…

(七)Tomcat源码阅读:Host组件分析

一、概述 Host类中比较重要的类就是HostConfig其它类实现的功能和之前的组件差不多&#xff0c;这里就不多介绍了。 二、阅读源码 1、HostConfig &#xff08;1&#xff09;重要方法 lifecycleEvent&#xff1a; 根据对应的方法设置对应的属性&#xff0c;并调用对应的方…

自己写gpt的软件教程-国内最好的chatgpt软件

GPT-3是一种非常强大的自然语言处理技术&#xff0c;可以为用户生成高质量的文本内容。虽然GPT-3最初是为英文而设计的&#xff0c;但是近年来&#xff0c;GPT-3在中文领域也变得越来越流行。在本篇教程中&#xff0c;我们将详细介绍如何在GPT-3中生成中文内容。 一、准备工作 …

第二天并发篇

一、线程状态 1.新建&#xff08;New&#xff09;&#xff1a;创建线程对象时 2.就绪&#xff08;Runnable&#xff09;&#xff1a;线程调用start方法&#xff0c;有执行资格没有执行权 3.运行&#xff1a;当就绪状态时抢到cpu的执行权之后&#xff0c;进入运行状态 4.阻塞&am…

过程控制系统中的模块技术MTP

在过程自动化行业中&#xff0c;模块化设备概念近年来越来越受欢迎。其中最热门的是MTP。MTP称为模块类型封装&#xff0c;它是过程工业自动化技术用户协会&#xff08;NAMUR&#xff09;提出的过程自动化行业的模块化标准&#xff0c;通过这种模型&#xff0c;开发工作的重点从…

C++(Qt)软件调试---linux下生成/调试Core文件(3)

#软件调试 C(Qt)软件调试—linux下生成/调试Core文件&#xff08;3&#xff09; 文章目录C(Qt)软件调试---linux下生成/调试Core文件&#xff08;3&#xff09;前言1、C生成Core和使用GDB调试1、环境2、C生成Core文件3、使用gdb工具调试core可定位段错误位置&#xff1b;4、修…

【创作赢红包】你是真的“C”——C语言中文件操作函数使用的详细讲解【上篇】

你是真的“c”——C语言中文件操作函数使用的详细讲解~&#x1f60e;前言&#x1f64c;一、 为什么使用文件&#xff1a;&#x1f64c;二、 什么是文件&#xff1a;&#x1f64c;2.1 程序文件2.2 数据文件2.3 文件名3. 文件的打开和关闭3.1 文件指针3.2 文件的打开和关闭4. 文件…

【ansible】实施任务控制

目录 实施任务控制 一&#xff0c;循环&#xff08;迭代&#xff09;--- loop 1&#xff0c;利用loop----item循环迭代任务 2&#xff0c;item---loop循环案例 1&#xff0c;定义item循环列表 2&#xff0c;通过变量应用列表格式 3&#xff0c;字典列表&#xff08;迭代嵌套子…

一个ESP32小东西

之前发了ESP8266&#xff0c;有人评论说玩下ESP32然后就买了几个回来&#xff0c;当然&#xff0c;也想着和大家一起玩介绍下这个开发板开发板Github项目链接https://github.com/Xinyuan-LilyGO/T-QT把仓库的代码下载到本地我们可以用ESP-IDF和Arduino两个SDK来开发ESP32S3ESP-…