当前位置:网站首页>动态刷新mapper看过来
动态刷新mapper看过来
2022-06-27 19:23:00 【高粱】
提供一个mybatisplus的mapper文件动态刷新配置类
方便开发时使用,不用每次修改xml文件后都要去重启应用
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;
// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInterceptor.setLimit(Integer.MAX_VALUE);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
// 设置方言
paginationInterceptor.setDialectType(DbType.MYSQL.getDb());
return paginationInterceptor;
}
/** * 自动刷新插件 * * @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;
/** * 切莫用于生产环境(后果自负), * <p>Mybatis 映射文件热加载(发生变动后自动重新加载).</p> * <p>方便开发时使用,不用每次修改xml文件后都要去重启应用.</p> */
public class MybatisMapperRefresh implements Runnable {
private static final Log logger = LogFactory.getLog(MybatisMapperRefresh.class);
/** * 记录jar包存在的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;
/** * 是否开启刷新mapper */
private boolean enabled;
/** * xml文件目录 */
private Set<String> fileSet;
/** * 延迟加载时间 */
private int delaySeconds = 10;
/** * 刷新间隔时间 */
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);
/* * 启动 XML 热加载 */
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) {
// 记录上次重新加载时间防止重复加载已经重载的文件
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();
}
}
/** * 刷新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();
}
}
/** * 清理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);
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);
}
}
}
}
}
/** * 清理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);
}
}
/** * 清理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);
}
}
}
最后配置文件配置
mybatis-plus.global-config.refresh = true
边栏推荐
- Experiment of love number lesson | phase V - emotion judgment of commodity review based on machine learning method
- oss上传调用的是哪个方法
- OpenSSL 编程 一:基本概念
- GoLand永久激活
- 数据平台调度升级改造 | 从Azkaban 平滑过度到Apache DolphinScheduler 的操作实践
- CORBA 架构体系指南(通用对象请求代理体系架构)
- Go从入门到实战——依赖管理(笔记)
- 数组作业题
- 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!"
- 请教CMS小程序首页的幻灯片在哪里设置?
猜你喜欢

强制 20 天内开发 APP 后集体被裁,技术负责人怒批:祝“早日倒闭!”

Go从入门到实战——CSP并发机制(笔记)

Unity3d button adapts the size according to the text content

White whoring red team goby & POC, how do you call white whoring?

Flexible IP network test tool -- x-launch

本周二晚19:00战码先锋第8期直播丨如何多方位参与OpenHarmony开源贡献

Flood fighting and disaster relief, overcoming difficulties, and City United premium products rushed to the aid of Yingde to donate loving materials

Use the storcli tool to configure raid. Just collect this article

Ceph分布式存储

流程控制任务
随机推荐
白嫖红队goby&POC,叫你如何白嫖?
MYSQL 性能优化 index 函数,隐藏,前缀,hash 索引 使用方法(2)
Serveur mandataire SQUID
Go从入门到实战——channel的关闭和广播(笔记)
mysql使用笔记一
覆盖接入2w+交通监测设备,EMQ 为深圳市打造交通全要素数字化新引擎
SQL必需掌握的100个重要知识点:使用函数处理数据
Share how I take notes
Acwing周赛57-最长连续子序列-(二分or树状数组)
KDD 2022 | graph neural network generalization framework under the paradigm of "pre training, prompting and fine tuning"
Educational Codeforces Round 108 (Rated for Div. 2)
Data platform scheduling upgrade and transformation | operation practice from Azkaban smooth transition to Apache dolphin scheduler
请教CMS小程序首页的幻灯片在哪里设置?
集合代码练习
Yu Wenwen, Hu Xia and other stars take you to play with the party. Pipi app ignites your summer
Go從入門到實戰——接口(筆記)
数据平台调度升级改造 | 从Azkaban 平滑过度到Apache DolphinScheduler 的操作实践
Let Ma Huateng down! Web3.0, hopeless
使用storcli工具配置RAID,收藏这一篇就够了
ARCS模型介绍