当前位置:网站首页>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
边栏推荐
- Acwing周赛57-最长连续子序列-(二分or树状数组)
- Go from introduction to practice -- coordination mechanism (note)
- Quick excel export according to customized excel Title Template
- [LeetCode]动态规划解分割数组II[Arctic Fox]
- STM32F107+LAN8720A使用STM32cubeMX配置网络连接+tcp主从机+UDP app
- [LeetCode]30. 串联所有单词的子串
- GBase 8a OLAP函数group by grouping sets的使用样例
- Array assignment
- 创建对象时JVM内存结构
- Codeforces Round #717 (Div. 2)
猜你喜欢

Go from entry to practice - dependency management (notes)

JVM memory structure when creating objects

Go从入门到实战——仅执行一次(笔记)

I think I should start writing my own blog.

STM32CubeIDE1.9.0\STM32CubeMX 6.5 F429IGT6加LAN8720A,配置ETH+LWIP

语言弱点列表--CWE,一个值得学习的网站
![[leetcode] dynamic programming solution partition array ii[arctic fox]](/img/a1/4644206db3e14c81f9f64e4da046bf.png)
[leetcode] dynamic programming solution partition array ii[arctic fox]

win11桌面出現“了解此圖片”如何删除

Null pointer exception

Codeforces Round #719 (Div. 3)
随机推荐
Go从入门到实战——package(笔记)
Stm32f107+lan8720a use stm32subemx to configure network connection +tcp master-slave +udp app
excel读取文件内容方法
Oracle migration MySQL unique index case insensitive don't be afraid
Array assignment
Slow bear market, bit Store provides stable stacking products to help you cross the bull and bear
GBase 8a OLAP分析函数cume_dist的使用样例
软件测试自动化测试之——接口测试从入门到精通,每天学习一点点
C语言程序设计详细版 (学习笔记1) 看完不懂,我也没办法。
Set code exercise
[LeetCode]161. 相隔为 1 的编辑距离
100 important knowledge points that SQL must master: combining where clauses
图解基于AQS队列实现的CountDownLatch和CyclicBarrier
GBase 8a OLAP函数group by grouping sets的使用样例
Codeforces Round #716 (Div. 2)
[leetcode] dynamic programming solution partition array ii[arctic fox]
vmware虚拟机PE启动
STM32F107+LAN8720A使用STM32cubeMX配置网络连接+tcp主从机+UDP app
Process control task
Tiktok's interest in e-commerce has hit the traffic ceiling?