All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mybatis.jpa.xml.XMLMapperLoader Maven / Gradle / Ivy

package com.mybatis.jpa.xml;

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.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;

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;

/**
 * xml 热加载 by jackwong
 * @ 本代码由https://my.oschina.net/houke/blog/901909 copy修改
 */
public class XMLMapperLoader implements DisposableBean, InitializingBean, ApplicationContextAware {

    private static final Logger logger = Logger.getLogger(XMLMapperLoader.class);

    private ApplicationContext applicationContext;
    private ScheduledExecutorService executorService;

    private Long initialDelay = 5L;

    private Long period = 5L;

    /**
     * 是否启用
     */
    private boolean enabled = true;

    private Resource[] mapperLocations;

    public XMLMapperLoader() {
        super();
        logger.debug("MyBatis xml文件热部署模块初始完成,生产模式需要移除");
    }

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

    public void afterPropertiesSet() throws Exception {
        if (enabled) {
            Map sqlSessionFactoryBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, SqlSessionFactory.class);
            if (sqlSessionFactoryBeans != null && sqlSessionFactoryBeans.size() > 0) {
                executorService = Executors.newScheduledThreadPool(sqlSessionFactoryBeans.size());
                for (SqlSessionFactory sqlSessionFactoryBean : sqlSessionFactoryBeans.values()) {
                    Scanner scanner = new Scanner(sqlSessionFactoryBean.getConfiguration(), mapperLocations);
                    executorService.scheduleAtFixedRate(scanner, initialDelay, period, TimeUnit.SECONDS);
                }
            }
        }
    }




    class Scanner implements Runnable {

        private Configuration configuration;
        private Resource[] mapperLocations;
        private HashMap mapperFiles = new HashMap();

        private Set loadedResources;
        private Map knownMappers;
        private Collection cacheNames;
        private Collection mappedStatementNames;
        private Collection parameterMapNames;
        private Collection resultMapNames;
        private Collection sqlFragmentNames;
        private Collection keyGeneratorNames;

        public Scanner(Configuration configuration, Resource[] mapperLocations) {
            this.configuration = configuration;
            this.mapperLocations = mapperLocations;
            loadedResources = getSetField(Configuration.class, configuration, "loadedResources");
            knownMappers = getMapField(MapperRegistry.class, configuration.getMapperRegistry(), "knownMappers");
            cacheNames = configuration.getCacheNames();
            mappedStatementNames = configuration.getMappedStatementNames();
            parameterMapNames = configuration.getParameterMapNames();
            resultMapNames = configuration.getResultMapNames();
            keyGeneratorNames = configuration.getKeyGeneratorNames();
            sqlFragmentNames = configuration.getSqlFragments().keySet();
            this.scan();
        }

        @Override
        public void run() {
            List resources = this.getChangedXml();
            if (resources != null && resources.size() > 0) {
                this.reloadXML(resources);
            }
        }

        public void reloadXML(List resources) {
            for (Resource mapperLocation : resources) {
                refresh(mapperLocation);
            }
        }

        private void refresh(Resource resource) {
            try {
                XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(), new XMLMapperEntityResolver());
                XNode context = xPathParser.evalNode("/mapper");
                String namespace = context.getStringAttribute("namespace");
                knownMappers.remove(Resources.classForName(namespace));
                loadedResources.remove(resource.toString());
                cacheNames.remove(namespace);
                cleanMappedStatements(context.evalNodes("select|insert|update"), namespace);
                cleanParameterMaps(context.evalNodes("/mapper/parameterMap"), namespace);
                cleanResultMaps(context.evalNodes("/mapper/resultMap"), namespace);
                cleanKeyGenerators(context.evalNodes("insert|update"), namespace);
                cleanSqlElements(context.evalNodes("/mapper/sql"), namespace);
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments());
                xmlMapperBuilder.parse();
                logger.debug("xml refresh success:" + namespace);
            } catch (Exception e) {
                logger.error("Refresh IOException :" + e.getMessage());
            } finally {
                ErrorContext.instance().reset();
            }
        }

        private Set getSetField(Class clazz, Object object, String fieldName) {
            try {
                Field setField = clazz.getDeclaredField(fieldName);
                setField.setAccessible(true);
                return (Set) setField.get(object);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }

        private Map getMapField(Class clazz, Object object, String fieldName) {
            try {
                Field setField = clazz.getDeclaredField(fieldName);
                setField.setAccessible(true);
                return (Map) setField.get(object);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }

        /**
         * 清理mappedStatements
         *
         * @param list
         * @param namespace
         */
        private void cleanMappedStatements(List list, String namespace) {
            for (XNode node : list) {
                String id = node.getStringAttribute("id");
                mappedStatementNames.remove(namespace + "." + id);
            }
        }

        /**
         * 清理parameterMap
         *
         * @param list
         * @param namespace
         */
        private void cleanParameterMaps(List list, String namespace) {
            for (XNode node : list) {
                String id = node.getStringAttribute("id");
                parameterMapNames.remove(namespace + "." + id);
            }
        }

        /**
         * 清理resultMap
         *
         * @param list
         * @param namespace
         */
        private void cleanResultMaps(List list, String namespace) {
            for (XNode node : list) {
                String id = node.getStringAttribute("id", node.getValueBasedIdentifier());
                resultMapNames.remove(id);
                resultMapNames.remove(namespace + "." + id);
                clearResultMaps(node, namespace);
            }
        }

        private void clearResultMaps(XNode xNode, String namespace) {
            for (XNode node : xNode.getChildren()) {
                if ("association".equals(node.getName()) || "collection".equals(node.getName()) || "case".equals(node.getName())) {
                    if (node.getStringAttribute("select") == null) {
                        resultMapNames.remove(node.getStringAttribute("id", node.getValueBasedIdentifier()));
                        resultMapNames.remove(namespace + "." + node.getStringAttribute("id", node.getValueBasedIdentifier()));
                        if (node.getChildren() != null && !node.getChildren().isEmpty()) {
                            clearResultMaps(node, namespace);
                        }
                    }
                }
            }
        }

        /**
         * 清理selectKey
         *
         * @param list
         * @param namespace
         */
        private void cleanKeyGenerators(List list, String namespace) {
            for (XNode node : list) {
                String id = node.getStringAttribute("id");
                keyGeneratorNames.remove(id + SelectKeyGenerator.SELECT_KEY_SUFFIX);
                keyGeneratorNames.remove(namespace + "." + id + SelectKeyGenerator.SELECT_KEY_SUFFIX);
            }
        }

        /**
         * 清理sql节点缓存
         *
         * @param list
         * @param namespace
         */
        private void cleanSqlElements(List list, String namespace) {
            for (XNode node : list) {
                String id = node.getStringAttribute("id");
                sqlFragmentNames.remove(id);
                sqlFragmentNames.remove(namespace + "." + id);
            }
        }

        public void scan() {
            if (!mapperFiles.isEmpty()) {
                return;
            }
            for (Resource mapperLocation : this.mapperLocations) {
                String fileKey = getValue(mapperLocation);
                mapperFiles.put(mapperLocation.getFilename(), fileKey);
            }
        }

        private String getValue(Resource resource) {
            try {
                String contentLength = String.valueOf((resource.contentLength()));
                String lastModified = String.valueOf((resource.lastModified()));
                return new StringBuilder(contentLength).append(lastModified).toString();
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }

        public List getChangedXml() {
            List resources = new ArrayList();
            for (Resource mapperLocation : this.mapperLocations) {
                if (mapperLocation == null) {
                    continue;
                }
                String name = mapperLocation.getFilename();
                String value = mapperFiles.get(name);
                String fileKey = getValue(mapperLocation);
                if (!fileKey.equals(value)) {
                    mapperFiles.put(name, fileKey);
                    resources.add(mapperLocation);
                }
            }
            return resources;
        }
    }

    public void destroy() throws Exception {
        if (executorService != null) {
            executorService.shutdownNow();
        }
    }
    public void setInitialDelay(Long initialDelay) {
        this.initialDelay = initialDelay;
    }

    public void setPeriod(Long period) {
        this.period = period;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public Resource[] getMapperLocations() {
        return mapperLocations;
    }

    public void setMapperLocations(Resource[] mapperLocations) {
        this.mapperLocations = mapperLocations;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy