当前位置:网站首页>Dynamic refresh mapper
Dynamic refresh mapper
2022-06-27 21:54:00 【sorghum】
Provide a mybatisplus Of mapper File dynamic refresh configuration class
Easy to use in development , No need to modify every time xml Restart the application after the file
package com.xxx.config;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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 com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.google.common.collect.Sets;
@Configuration
@EnableTransactionManagement
@MapperScan({
"com.xxx.*.dao", "com.xxx.dao" })
@ConditionalOnClass(PaginationInterceptor.class)
public class MyBatisPlusConfig {
@Autowired
private MybatisPlusProperties mybatisPlusProperties;
// Paging plug-ins
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// Set the maximum number of single page restrictions , Default 500 strip ,-1 There is no limit on the
paginationInterceptor.setLimit(Integer.MAX_VALUE);
// Turn on count Of join Optimize , Only for part left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
// Set dialect
paginationInterceptor.setDialectType(DbType.MYSQL.getDb());
return paginationInterceptor;
}
/** * Auto refresh plug-in * * @return */
@ConditionalOnProperty("mybatis-plus.global-config.refresh")
@Bean
public MybatisMapperRefresh mybatisMapperRefresh(ApplicationContext applicationContext,
SqlSessionFactory sqlSessionFactory) {
Set<Resource> mapperLocations = Sets.newLinkedHashSet();
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;
}
}
package com.xxx.config;
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;
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 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;
/** * Never use in a production environment ( Consequence conceit ), * <p>Mybatis Mapping file hot load ( Automatically reload after changes ).</p> * <p> Easy to use in development , No need to modify every time xml Restart the application after the file .</p> */
public class MybatisMapperRefresh implements Runnable {
private static final Log logger = LogFactory.getLog(MybatisMapperRefresh.class);
/** * Record jar Package exists 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;
/** * Do you want to enable refresh mapper */
private boolean enabled;
/** * xml File directory */
private Set<String> fileSet;
/** * Delay load time */
private int delaySeconds = 10;
/** * Refresh interval */
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);
/* * start-up XML Thermal loading */
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) {
// Record the last reload time to prevent reloading files that have been reloaded
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();
}
}
/** * Refresh 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"), 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();
}
}
/** * clear 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);
}
}
/** * clear 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);
}
}
}
}
}
/** * clear 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);
}
}
/** * clear sql Node cache * * @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);
}
}
}
Finally, the configuration file configures
mybatis-plus.global-config.refresh = true
边栏推荐
- 读写分离-Mysql的主从复制
- 小王的面试训练任务
- . Net learning notes (V) -- lambda, LINQ, anonymous class (VaR), extension method
- Process control task
- Golang 使用正则来匹配出子字符串函数
- The create database of gbase 8A takes a long time to query and is suspected to be stuck
- Go from introduction to actual combat - panic and recover (notes)
- Go from introduction to practice - Interface (notes)
- Have time to look at ognl expressions
- Open source technology exchange - Introduction to Chengying, a one-stop fully automated operation and maintenance manager
猜你喜欢

创建对象时JVM内存结构

洛谷P5706 再分肥宅水

C语言程序设计详细版 (学习笔记1) 看完不懂,我也没办法。

01-Golang-环境搭建

我想我要开始写我自己的博客了。

Set code exercise

Special training of guessing game

开源技术交流丨一站式全自动化运维管家ChengYing入门介绍

快递e栈——数组篇小型项目

After being forced to develop the app within 20 days, the group was laid off, and the technical director angrily criticized it: I wish "closure as soon as possible!"
随机推荐
Common methods of string class
Array assignment
Have time to look at ognl expressions
gomock mockgen : unknown embedded interface
[leetcode] dynamic programming solution partition array ii[arctic fox]
ABC-Teleporter Setting-(思维+最短路)
微服务之远程调用
Oracle migration MySQL unique index case insensitive don't be afraid
Go从入门到实战——Context与任务取消(笔记)
io流代码
动态刷新mapper看过来
流程控制任务
Go from introduction to actual combat - context and task cancellation (notes)
Go from introduction to actual combat -- channel closing and broadcasting (notes)
matlab查找某一行或者某一列在矩阵中的位置
Go from starting to Real - Interface (note)
∫(0→1) ln(1+x) / (x ² + 1) dx
图解基于AQS队列实现的CountDownLatch和CyclicBarrier
Golang 使用正则来匹配出子字符串函数
Go从入门到实战——Panic和recover(笔记)