
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