开发的时候,写Mybatis Mapper.xml文件的时候,每次修改SQL都需要重启服务,感觉十分麻烦,于是尝试写了一个Mybatis的Mapper.xml热加载。

能在修改Mapper.xml之后重新加载Mybatis,开发的时候可以用一下。

Spring配置:

<bean id="MybatisMapperDynamicLoader" class="com.teststartup.MybatisMapperDynamicLoader" />

Java代码:

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask; import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver; public class MybatisMapperDynamicLoader implements InitializingBean, ApplicationContextAware { private final HashMap<String, String> mappers = new HashMap<String, String>();
private volatile ConfigurableApplicationContext context = null;
private volatile Scanner scanner = null; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = (ConfigurableApplicationContext) applicationContext;
} @Override
public void afterPropertiesSet() throws Exception {
try {
scanner = new Scanner();
new Timer(true).schedule(new TimerTask() {
public void run() {
try {
if (scanner.isChanged()) {
System.out.println("load mapper.xml");
scanner.reloadXML();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, 10 * 1000, 5 * 1000);
} catch (Exception e1) {
e1.printStackTrace();
}
} @SuppressWarnings("unchecked")
class Scanner {
private static final String XML_RESOURCE_PATTERN = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "**/*Sql.xml";
private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
public Scanner() throws IOException {
Resource[] resources = findResource();
if (resources != null) {
for (Resource resource : resources) {
String key = resource.getURI().toString();
String value = getMd(resource);
mappers.put(key, value);
}
}
}
public void reloadXML() throws Exception {
SqlSessionFactory factory = context.getBean(SqlSessionFactory.class);
Configuration configuration = factory.getConfiguration();
removeConfig(configuration);
for (Resource resource : findResource()) {
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), configuration, resource.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} finally {
ErrorContext.instance().reset();
}
}
}
private void removeConfig(Configuration configuration) throws Exception {
Class<?> classConfig = configuration.getClass();
clearMap(classConfig, configuration, "mappedStatements");
clearMap(classConfig, configuration, "caches");
clearMap(classConfig, configuration, "resultMaps");
clearMap(classConfig, configuration, "parameterMaps");
clearMap(classConfig, configuration, "keyGenerators");
clearMap(classConfig, configuration, "sqlFragments");
clearSet(classConfig, configuration, "loadedResources");
}
private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {
Field field = classConfig.getDeclaredField(fieldName);
field.setAccessible(true);
((Map) field.get(configuration)).clear();
}
private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {
Field field = classConfig.getDeclaredField(fieldName);
field.setAccessible(true);
((Set) field.get(configuration)).clear();
}
public boolean isChanged() throws IOException {
boolean isChanged = false;
for (Resource resource : findResource()) {
String key = resource.getURI().toString();
String value = getMd(resource);
if (!value.equals(mappers.get(key))) {
isChanged = true;
mappers.put(key, value);
}
}
return isChanged;
}
private Resource[] findResource() throws IOException {
return resourcePatternResolver.getResources(XML_RESOURCE_PATTERN);
}
private String getMd(Resource resource) throws IOException {
return new StringBuilder().append(resource.contentLength()).append("-").append(resource.lastModified()).toString();
}
}
}

Mybatis热加载Mapper.xml的更多相关文章

  1. mybatis plus3.1.0 热加载mapper

    今天又开始写业务代码了,每次修改SQL都要重启服务,实在是浪费时间. 想起之前研究过的<mybatis plus3.1.0 热加载mapper>,一直没有成功,今天静下心来分析了问题,终于 ...

  2. DB数据源之SpringBoot+MyBatis踏坑过程(二)手工配置数据源与加载Mapper.xml扫描

    DB数据源之SpringBoot+MyBatis踏坑过程(二)手工配置数据源与加载Mapper.xml扫描 liuyuhang原创,未经允许进制转载  吐槽之后应该有所改了,该方式可以作为一种过渡方式 ...

  3. DB数据源之SpringBoot+MyBatis踏坑过程(三)手工+半自动注解配置数据源与加载Mapper.xml扫描

    DB数据源之SpringBoot+MyBatis踏坑过程(三)手工+半自动注解配置数据源与加载Mapper.xml扫描 liuyuhang原创,未经允许禁止转载    系列目录连接 DB数据源之Spr ...

  4. 在mybatis 中批量加载mapper.xml

    可以直接加载一个包文件名,将这个包里的所有*mapper.xml文件加载进来. 指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载: 必须按一定的标准:即xml文件和 ...

  5. mybatis热加载的实现

    最近在使用mybatis,由于是刚刚开始用,用的并不顺手,目前是感觉有2个地方非常的不好用: 1.mybatis调试不方便 由于dao层只有接口,实现只是一个map的xml文件,想加断点都没有地方加, ...

  6. (转)mybatis热加载(依赖mybatis-plus插件)的实现

    最近在使用mybatis,由于是刚刚开始用,用的并不顺手,目前是感觉有2个地方非常的不好用: 1.mybatis调试不方便 由于dao层只有接口,实现只是一个map的xml文件,想加断点都没有地方加, ...

  7. 抛开 Spring ,你知道 MyBatis 加载 Mapper 的底层原理吗?

    原文链接:抛开 Spring ,你知道 MyBatis 加载 Mapper 的底层原理吗? 大家都知道,利用 Spring 整合 MyBatis,我们可以直接利用 @MapperScan 注解或者 @ ...

  8. 基于JRebel开发的MybatisPlus热加载插件

    前言 前天项目中使用了mybatis-plus,但是搭配Jrebel开发项目时,发现修改mapper的xml,或者mapper方法中的注解,Jrebel并没有能够reload mapper.于是就有了 ...

  9. mybatis配置加载源码概述

    Mybatis框架里,有两种配置文件,一个是全局配置文件config.xml,另一个是对应每个表的mapper.xml配置文件.Mybatis框架启动时,先加载config.xml, 在加载每个map ...

随机推荐

  1. 关于Java的File类、字节流和字符流

    一.File类: 在Windows下的路径分隔符(\)和在Linux下的路径分隔符(/)是不一样的,当直接使用绝对路径时,跨平台会报No Such file or diretory异常. File中还 ...

  2. 【云计算】使用supervisor管理Docker多进程-ntpd+uwsgi+nginx示例最佳实践

    supervisor安装启动: apt-get install supervisor -y # start supervisord nodaemon /usr/bin/supervisord --no ...

  3. Spark SQL 代码简要阅读(基于Spark 1.1.0)

    Spark SQL允许相关的查询如SQL,HiveQL或Scala运行在spark上.其核心组件是一个新的RDD:SchemaRDD,SchemaRDDs由行对象组成,并包含一个描述此行对象的每一列的 ...

  4. 【笔记】js原生方法 在元素外部或内部实现添加元素功能(类似jq 的 insert 和 append)

    介绍的这个方法是:insetAdjacentHTML() 方法 此方法接收两个参数: 第一个参数必为下列值: beforebegin:在调用的元素外部的前面添加一个目标元素 afterend:在调用元 ...

  5. 我的mini_c语言文法设计

    //这个文件主要是用来描述当前源语言的词法结构和语法结构 //当前语言是c语言的一个子集,因此里面所有的描述大家都很熟悉 //注意,当前语言并不支持预处理,因为c预处理比较复杂,而且楼主能力低下,因此 ...

  6. 【ACM】Largest prime factor

    /*打表把素数能组合的数先设置成相应的位数*/ /* if n equals two and n is No.1 position of prime factors  so four position ...

  7. vue - 选项

    1.计算属性(computed):主要是对原数据进行改造输出.改造输出:包括格式化数据(价格,日期),大小写转换,排序,添加符号 2.methods(methods):用于绑定html中的事件对应的方 ...

  8. 【DB2】查询上月末、上年末、上年同期等信息

    此处以20180612为例子 想得到上年末.上年同期.上月末这些时间点,只需要记住函数ADD_MONTHS.LAST_DAY.ADD_YEARS这些函数即可. 上年末 SELECT SUBSTR(TO ...

  9. 在DATASET中要是想添加进另一个表怎么办?

    问:sql="select * from banzhu_manage ";adapter=new SqlDataAdapter(sql,banzhu_conn);adapter.F ...

  10. 抛弃【 LIMIT O,N 】,换种方法查询分页

    在分页功能开发时,我们很习惯用LIMIT O,N的方法来取数据.这种方法在遇到超大分页偏移量时是会把MySQL搞死的ooo... 通常,我们会采用ORDER BY LIMIT start, offse ...