mybatis mapper.xml 热部署

简介: package com.guangeryi.mall.core.utils.mybatis;import com.guangeryi.mall.common.CommonConstant;import com.
package com.guangeryi.mall.core.utils.mybatis;

import com.guangeryi.mall.common.CommonConstant;
import com.guangeryi.mall.core.utils.StringUtils;
import org.apache.ibatis.binding.MapperProxyFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class MyBatisMapperRefresher implements DisposableBean, InitializingBean, ApplicationContextAware {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 扫描周期,单位秒
     */
    private int periodSeconds = 5;
    /**
     * 初始化完成,延迟扫描时间,单位秒
     */
    private int initialDelay = 5;
    /**
     * 是否启用
     */
    @Value("${mapper.auto.load}")
    private boolean enabled;
    private ConfigurableApplicationContext context = null;
    private transient Resource[] basePackage = null;
    private HashMap<String, String> fileMapping = new HashMap<>();
    private Set<String> isChangedMapper = new HashSet<>();
    private Scanner scanner = null;
    private ScheduledExecutorService service = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = (ConfigurableApplicationContext) applicationContext;

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (!enabled) {
            logger.info("MyBatisMapperRefresher is Disabled");
            return;
        }

        try {
            service = Executors.newScheduledThreadPool(1);
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            basePackage = resolver.getResources("classpath*:xx.xml");// 加载配置xml文件
            logger.info("监控以下" + basePackage.length + "个xml文件:");
            // 触发文件监听事件
            scanner = new Scanner();
            scanner.scan();
            service.scheduleAtFixedRate(new Task(), MyBatisMapperRefresher.this.initialDelay, MyBatisMapperRefresher.this.periodSeconds, TimeUnit.SECONDS);

        } catch (Exception e1) {
            logger.error("can not starter Mybatis xml refresher,exception:{}", e1);
        }

    }

    class Task implements Runnable {
        @Override
        public void run() {
            try {
                if (scanner.isChanged()) {
                    scanner.reloadXML();
                }
            } catch (Exception ex) {
                logger.error("MyBatisMapperRefresher,exception:{}", ex);
            }
        }
    }

    @Override
    public void destroy() throws Exception {
        if (service != null) {
            service.shutdownNow();
        }
    }

    class Scanner {
        private Resource[] mapperXmlFiles;

        public Scanner() {
            mapperXmlFiles = MyBatisMapperRefresher.this.basePackage;
        }

        public void reloadXML() throws Exception {
            SqlSessionFactory factory = context.getBean(SqlSessionFactory.class);
            Configuration configuration = factory.getConfiguration();
            Set<Class<?>> knownMapperKeys = new HashSet<>();
            Field field = configuration.getMapperRegistry().getClass().getDeclaredField("knownMappers");
            field.setAccessible(true);
            Map<Class<?>, MapperProxyFactory<?>> knownMappers = (Map<Class<?>, MapperProxyFactory<?>>) field.get(configuration.getMapperRegistry());
            knownMapperKeys.addAll(knownMappers.keySet());
            // 移除加载项
            removeConfig(configuration, isChangedMapper);

            Set<String> isChangedMapperTemp = isChangedMapper.stream().map(item -> StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet());
            Iterator<Class<?>> classIterator = knownMapperKeys.iterator();
            while (classIterator.hasNext()) {
                Class clazz = classIterator.next();
                if (clazz == null || Strings.isEmpty(clazz.getName()))
                    continue;
                String[] clazzNames = StringUtils.split(clazz.getName(), CommonConstant.CHAR_SPOT);
                if (isChangedMapperTemp.contains(clazzNames[clazzNames.length - 1])) {
                    knownMappers.remove(clazz);
                    configuration.addMapper(clazz);
                }
            }
            isChangedMapper.clear();
        }


        private void removeConfig(Configuration configuration, Set<String> isChangedMapper) throws Exception {
            Class<?> classConfig = configuration.getClass();
            clearMap(classConfig, configuration, "mappedStatements", isChangedMapper);
            clearMap(classConfig, configuration, "caches", isChangedMapper);
            clearMap(classConfig, configuration, "resultMaps", isChangedMapper);
            clearMap(classConfig, configuration, "parameterMaps", isChangedMapper);
            clearMap(classConfig, configuration, "keyGenerators", isChangedMapper);
            clearMap(classConfig, configuration, "sqlFragments", isChangedMapper);
            clearSet(classConfig, configuration, "loadedResources", isChangedMapper);

        }

        private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName, Set<String> isChangedMapper) throws Exception {
            Set<String> isChangedMapperTemp = isChangedMapper.stream().map(item -> "repository." + StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet());
            Field field = classConfig.getDeclaredField(fieldName);
            field.setAccessible(true);
            Map mapConfig = (Map) field.get(configuration);
            Map tempMap = new HashMap();
            tempMap.putAll(mapConfig);
            tempMap.forEach((key, value) -> {
                if (StringUtils.StringContains((String) key, isChangedMapperTemp))
                    mapConfig.remove(key);
            });

        }

        private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName, Set<String> isChangedMapper) throws Exception {
            Set<String> isChangedMapperTemp = isChangedMapper.stream().map(item -> "repository/" + StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet());
            isChangedMapperTemp.addAll(isChangedMapper.stream().map(item -> "repository." + StringUtils.split(item, CommonConstant.CHAR_SPOT)[0]).collect(Collectors.toSet()));
            Field field = classConfig.getDeclaredField(fieldName);
            field.setAccessible(true);
            Set setConfig = (Set) field.get(configuration);
            Set tempMap = new HashSet<>();
            tempMap.addAll(setConfig);
            tempMap.forEach(item -> {
                if (StringUtils.StringContains((String) item, isChangedMapperTemp))
                    setConfig.remove(item);
            });
        }

        public void scan() throws IOException {
            if (!fileMapping.isEmpty()) {
                return;
            }

            Resource[] resources = mapperXmlFiles;
            if (resources != null) {
                for (int i = 0; i < resources.length; i++) {
                    String multi_key = getValue(resources[i]);
                    String fileName = resources[i].getFilename();
                    fileMapping.put(fileName, multi_key);
                    logger.info("monitor Mybatis mapper file:{}", resources[i].getFile().getAbsolutePath());
                }
            }

        }

        private String getValue(Resource resource) throws IOException {
            String contentLength = String.valueOf((resource.contentLength()));
            String lastModified = String.valueOf((resource.lastModified()));
            return new StringBuilder(contentLength).append(lastModified).toString();
        }

        public boolean isChanged() throws IOException {
            boolean isChanged = false;

            Resource[] resources = mapperXmlFiles;
            if (resources != null) {
                for (int i = 0; i < resources.length; i++) {
                    String name = resources[i].getFilename();
                    String value = fileMapping.get(name);
                    String multi_key = getValue(resources[i]);
                    if (!multi_key.equals(value)) {
                        isChanged = true;
                        fileMapping.put(name, multi_key);
                        isChangedMapper.add(name);
                        logger.info("reload Mybatis mapper file:{}", resources[i].getFile().getAbsolutePath());
                    }
                }
            }
            return isChanged;
        }

    }

}

希望园友多多提意见...

目录
相关文章
|
5月前
|
XML Java 数据库连接
mybatis中在xml文件中通用查询结果列如何使用
mybatis中在xml文件中通用查询结果列如何使用
360 0
|
4月前
|
XML Java 数据库连接
MyBatis入门——MyBatis XML配置文件(3)
MyBatis入门——MyBatis XML配置文件(3)
59 6
|
1月前
|
SQL XML Java
mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法
当然,这些仅是MyBatis功能的初步介绍。MyBatis还提供了高级特性,如动态SQL、类型处理器、插件等,可以进一步提供对数据库交互的强大支持和灵活性。希望上述内容对您理解MyBatis的基本操作有所帮助。在实际使用中,您可能还需要根据具体的业务要求调整和优化SQL语句和配置。
39 1
|
2月前
|
SQL Java 数据库连接
MyBatis Mapper.XML 标签使用说明
MyBatis Mapper.XML 标签使用说明
30 0
|
4月前
|
SQL XML Java
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
67 3
|
3月前
|
XML Java 数据格式
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法
|
4月前
|
XML Java 数据库连接
MyBatis第二课,灰度发布,@Results注解,使用xml书写mysql
MyBatis第二课,灰度发布,@Results注解,使用xml书写mysql
|
4月前
|
XML 关系型数据库 数据库
使用mybatis-generator插件生成postgresql数据库model、mapper、xml
使用mybatis-generator插件生成postgresql数据库model、mapper、xml
428 0
|
5月前
|
XML Java 数据库连接
Mybatis逆向工程的2种方法,一键高效快速生成Pojo、Mapper、XML,摆脱大量重复开发
【5月更文挑战第10天】Mybatis逆向工程的2种方法,一键高效快速生成Pojo、Mapper、XML,摆脱大量重复开发
63 6
|
5月前
|
XML Java 数据库连接
MyBatis 解决上篇的参数绑定问题以及XML方式交互
MyBatis 解决上篇的参数绑定问题以及XML方式交互
57 0