Java tutorial
/** * Copyright (c) 2011-2020, hubin (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.spring; import com.baomidou.mybatisplus.entity.GlobalConfiguration; import com.baomidou.mybatisplus.toolkit.SystemClock; 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.parsing.XNode; import org.apache.ibatis.parsing.XPathParser; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.core.NestedIOException; 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.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * <p> * ?<br> * Mybatis ????.<br> * ???xml????.<br> * </p> * * @author nieqiurong * @Date 2016-08-25 */ public class MybatisMapperRefresh implements Runnable { private static final Log logger = LogFactory.getLog(MybatisMapperRefresh.class); private SqlSessionFactory sqlSessionFactory; private Resource[] mapperLocations; private Long beforeTime = 0L; private Configuration configuration; /** * ??mapper */ private boolean enabled; /** * xml */ private Set<String> fileSet; /** * */ private int delaySeconds = 10; /** * */ private int sleepSeconds = 20; /** * jarmapper */ private static Map<String, List<Resource>> jarMapper = new HashMap<String, List<Resource>>(); public MybatisMapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, int delaySeconds, int sleepSeconds, boolean enabled) { this.mapperLocations = mapperLocations; 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; this.sqlSessionFactory = sqlSessionFactory; this.enabled = enabled; this.configuration = sqlSessionFactory.getConfiguration(); this.run(); } public void run() { final GlobalConfiguration mybatisGlobalCache = GlobalConfiguration.GlobalConfig(configuration); /* * ? XML */ if (enabled) { beforeTime = SystemClock.now(); final MybatisMapperRefresh runnable = this; new Thread(new Runnable() { public void run() { if (fileSet == null) { fileSet = new HashSet<String>(); 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<Resource>(); 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(); } while (true) { try { for (String filePath : fileSet) { File file = new File(filePath); if (file != null && file.isFile() && file.lastModified() > beforeTime) { mybatisGlobalCache.setRefresh(true); List<Resource> removeList = jarMapper.get(filePath); if (removeList != null && !removeList.isEmpty()) {// jarxmljarxml??jar?xml for (Resource resource : removeList) { runnable.refresh(resource); } } else { runnable.refresh(new FileSystemResource(file)); } } } if (mybatisGlobalCache.isRefresh()) { beforeTime = SystemClock.now(); } mybatisGlobalCache.setRefresh(true); } catch (Exception exception) { exception.printStackTrace(); } try { Thread.sleep(sleepSeconds * 1000); } catch (InterruptedException interruptedException) { interruptedException.printStackTrace(); } } } }, "mybatis-plus MapperRefresh").start(); } } /** * mapper * * @throws Exception */ @SuppressWarnings("rawtypes") private void refresh(Resource resource) throws Exception { 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()); 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"), namespace); cleanSqlElement(context.evalNodes("/mapper/sql"), namespace); XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), sqlSessionFactory.getConfiguration(), // sql?? resource.toString(), sqlSessionFactory.getConfiguration().getSqlFragments()); xmlMapperBuilder.parse(); logger.debug("refresh:" + resource + ",success!"); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e); } 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); } } /** * ?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); } } /** * ?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); } } }