springboot mybatis 热加载mapper.xml文件(最简单)

大家好,我是烤鸭:

    今天介绍一下springboot mybatis 热加载mapper.xml文件。

    本来不打算写的,看到网上比较流行的方式都比较麻烦,想着简化一下。

    网上流行的版本。

    https://www.cnblogs.com/oskyhg/p/8587701.html

    总结一下需要:mybatis-config,mybatis-refresh.properties,MapperRefresh.java,SqlSessionFactoryBean.java

    按照这个博客写的,确实挺好用的。但是,springboot简便就是简便在没有配置文件。

    于是看看能不能优化一下。优化后只需要mybatis-refresh.propertiesMapperRefresh.java+一行代码

环境:

    springboot     2.0.0.RELEASE    

    mybatis    3.4.4

    mybatis-spring-boot-starter    1.3.0

1.    MapperRefresh.java(同上,复制)

        定时读取指定目录下的mapper.xml文件,是否被修改

package com.xxx.web.common.config.mybatis;import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;  
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
import java.util.Properties;  
import java.util.Set;import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;  
import org.apache.ibatis.executor.ErrorContext;  
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NestedIOException;  
import org.springframework.core.io.Resource;  
import com.google.common.collect.Sets;
/** * 刷新MyBatis Mapper XML 线程 * @author ThinkGem * @version 2016-5-29 */  
public class MapperRefresh implements java.lang.Runnable {  public static Logger log = LoggerFactory.getLogger(MapperRefresh.class);  private static String filename = "mybatis-refresh.properties";  private static Properties prop = new Properties();  private static boolean enabled;         // 是否启用Mapper刷新线程功能  private static boolean refresh;         // 刷新启用后,是否启动了刷新线程  private Set<String> location;         // Mapper实际资源路径  private Resource[] mapperLocations;     // Mapper资源路径  private Configuration configuration;        // MyBatis配置对象  private Long beforeTime = 0L;           // 上一次刷新时间  private static int delaySeconds;        // 延迟刷新秒数  private static int sleepSeconds;        // 休眠时间  private static String mappingPath;      // xml文件夹匹配字符串,需要根据需要修改  static {  //        try {  
//            prop.load(MapperRefresh.class.getResourceAsStream(filename));  
//        } catch (Exception e) {  
//            e.printStackTrace();  
//            System.out.println("Load mybatis-refresh “"+filename+"” file error.");  
//        }  URL url = MapperRefresh.class.getClassLoader().getResource(filename);InputStream is;try {is = url.openStream();if (is == null) {log.warn("applicationConfig.properties not found.");} else {prop.load(is);} } catch (IOException e) {e.printStackTrace();}String value = getPropString("enabled");System.out.println(value);enabled = "true".equalsIgnoreCase(value);  delaySeconds = getPropInt("delaySeconds");  sleepSeconds = getPropInt("sleepSeconds");  mappingPath = getPropString("mappingPath");  delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;  sleepSeconds = sleepSeconds == 0 ? 3 : sleepSeconds;  mappingPath = StringUtils.isBlank(mappingPath) ? "mappings" : mappingPath;  log.debug("[enabled] " + enabled);  log.debug("[delaySeconds] " + delaySeconds);  log.debug("[sleepSeconds] " + sleepSeconds);  log.debug("[mappingPath] " + mappingPath);  }  public static boolean isRefresh() {  return refresh;  }  public MapperRefresh(Resource[] mapperLocations, Configuration configuration) {  this.mapperLocations = mapperLocations;  this.configuration = configuration;  }  @Override  public void run() {  beforeTime = System.currentTimeMillis();  log.debug("[location] " + location);  log.debug("[configuration] " + configuration);  if (enabled) {  // 启动刷新线程  final MapperRefresh runnable = this;  new Thread(new java.lang.Runnable() {  @Override  public void run() {  if (location == null){  location = Sets.newHashSet();  log.debug("MapperLocation's length:" + mapperLocations.length);  for (Resource mapperLocation : mapperLocations) {  String s = mapperLocation.toString().replaceAll("\\\\", "/");  s = s.substring("file [".length(), s.lastIndexOf(mappingPath) + mappingPath.length());  if (!location.contains(s)) {  location.add(s);  log.debug("Location:" + s);  }  }  log.debug("Locarion's size:" + location.size());  }  try {  Thread.sleep(delaySeconds * 1000);  } catch (InterruptedException e2) {  e2.printStackTrace();  }  refresh = true;  System.out.println("========= Enabled refresh mybatis mapper =========");  while (true) {  try {  for (String s : location) {  runnable.refresh(s, beforeTime);  }  } catch (Exception e1) {  e1.printStackTrace();  }  try {  Thread.sleep(sleepSeconds * 1000); } catch (InterruptedException e) {  e.printStackTrace();  }  }  }  }, "MyBatis-Mapper-Refresh").start();  }  }  /** * 执行刷新 * @param filePath 刷新目录 * @param beforeTime 上次刷新时间 * @throws NestedIOException 解析异常 * @throws FileNotFoundException 文件未找到 * @author ThinkGem */  @SuppressWarnings({ "rawtypes", "unchecked" })  private void refresh(String filePath, Long beforeTime) throws Exception {  // 本次刷新时间  Long refrehTime = System.currentTimeMillis();  // 获取需要刷新的Mapper文件列表  List<File> fileList = this.getRefreshFile(new File(filePath), beforeTime);  if (fileList.size() > 0) {  log.debug("Refresh file: " + fileList.size());  }  for (int i = 0; i < fileList.size(); i++) {  InputStream inputStream = new FileInputStream(fileList.get(i));  String resource = fileList.get(i).getAbsolutePath();  try {  // 清理原有资源,更新为自己的StrictMap方便,增量重新加载  String[] mapFieldNames = new String[]{  "mappedStatements", "caches",  "resultMaps", "parameterMaps",  "keyGenerators", "sqlFragments"  };  for (String fieldName : mapFieldNames){  Field field = configuration.getClass().getDeclaredField(fieldName);  field.setAccessible(true);  Map map = ((Map)field.get(configuration));  if (!(map instanceof StrictMap)){  Map newMap = new StrictMap(StringUtils.capitalize(fieldName) + "collection");  for (Object key : map.keySet()){  try {  newMap.put(key, map.get(key));  }catch(IllegalArgumentException ex){  newMap.put(key, ex.getMessage());  }  }  field.set(configuration, newMap);  }  }  // 清理已加载的资源标识,方便让它重新加载。  Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");  loadedResourcesField.setAccessible(true);  Set loadedResourcesSet = ((Set)loadedResourcesField.get(configuration));  loadedResourcesSet.remove(resource);  //重新编译加载资源文件。  XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, configuration,   resource, configuration.getSqlFragments());  xmlMapperBuilder.parse();  } catch (Exception e) {  throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);  } finally {  ErrorContext.instance().reset();  }  
//            System.out.println("Refresh file: " + mappingPath + StringUtils.substringAfterLast(fileList.get(i).getAbsolutePath(), mappingPath));  if (log.isDebugEnabled()) {  log.debug("Refresh file: " + fileList.get(i).getAbsolutePath());  log.debug("Refresh filename: " + fileList.get(i).getName());  }  }  // 如果刷新了文件,则修改刷新时间,否则不修改  if (fileList.size() > 0) {  this.beforeTime = refrehTime;  }  }  /** * 获取需要刷新的文件列表 * @param dir 目录 * @param beforeTime 上次刷新时间 * @return 刷新文件列表 */  private List<File> getRefreshFile(File dir, Long beforeTime) {  List<File> fileList = new ArrayList<File>();  File[] files = dir.listFiles();  if (files != null) {  for (int i = 0; i < files.length; i++) {  File file = files[i];  if (file.isDirectory()) {  fileList.addAll(this.getRefreshFile(file, beforeTime));  } else if (file.isFile()) {  if (this.checkFile(file, beforeTime)) {  fileList.add(file);  }  } else {  System.out.println("Error file." + file.getName());  }  }  }  return fileList;  }  /** * 判断文件是否需要刷新 * @param file 文件 * @param beforeTime 上次刷新时间 * @return 需要刷新返回true,否则返回false */  private boolean checkFile(File file, Long beforeTime) {  if (file.lastModified() > beforeTime) {  return true;  }  return false;  }  /** * 获取整数属性 * @param key * @return */  private static int getPropInt(String key) {  int i = 0;  try {  i = Integer.parseInt(getPropString(key));  } catch (Exception e) {  }  return i;  }  /** * 获取字符串属性 * @param key * @return */  private static String getPropString(String key) {  return prop == null ? null : prop.getProperty(key).trim();  }  /** * 重写 org.apache.ibatis.session.Configuration.StrictMap 类 * 来自 MyBatis3.4.0版本,修改 put 方法,允许反复 put更新。 */  public static class StrictMap<V> extends HashMap<String, V> {  private static final long serialVersionUID = -4950446264854982944L;  private String name;  public StrictMap(String name, int initialCapacity, float loadFactor) {  super(initialCapacity, loadFactor);  this.name = name;  }  public StrictMap(String name, int initialCapacity) {  super(initialCapacity);  this.name = name;  }  public StrictMap(String name) {  super();  this.name = name;  }  public StrictMap(String name, Map<String, ? extends V> m) {  super(m);  this.name = name;  }  @SuppressWarnings("unchecked")  public V put(String key, V value) {  // ThinkGem 如果现在状态为刷新,则刷新(先删除后添加)  if (MapperRefresh.isRefresh()) {  remove(key);  
//                MapperRefresh.log.debug("refresh key:" + key.substring(key.lastIndexOf(".") + 1));  }  // ThinkGem end  if (containsKey(key)) {  throw new IllegalArgumentException(name + " already contains value for " + key);  }  if (key.contains(".")) {  final String shortKey = getShortName(key);  if (super.get(shortKey) == null) {  super.put(shortKey, value);  } else {  super.put(shortKey, (V) new Ambiguity(shortKey));  }  }  return super.put(key, value);  }  public V get(Object key) {  V value = super.get(key);  if (value == null) {  throw new IllegalArgumentException(name + " does not contain value for " + key);  }  if (value instanceof Ambiguity) {  throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name  + " (try using the full name including the namespace, or rename one of the entries)");  }  return value;  }  private String getShortName(String key) {  final String[] keyparts = key.split("\\.");  return keyparts[keyparts.length - 1];  }  protected static class Ambiguity {  private String subject;  public Ambiguity(String subject) {  this.subject = subject;  }  public String getSubject() {  return subject;  }  }  }  
}

2.    mybatis-refresh.properties(同上,复制)

       设置读取配置文件的参数,定时和频率,是否多线程

enabled=true
delaySeconds=30
sleepSeconds=10
mappingPath=mybatis

 3.     关于sqlSessionFactory

        网上大多的实现方式都是重新创建SqlSessionFactory,然后再注入。

        类似这样:

       

        我的疑问:

        1.    MapperRefresh需要当前sqlSessionBean的configuration。既然springboot都已经sqlSessionFactory把创建好了,直接获取sqlSessionBean的configuration就好了么。

        2.    yml配置mybatis的时候,没有sqlMapConfig(myabtis-config).xml这个配置文件,还得先创建一个,很不方便。试着把配置文件那行注释掉,会报错,Location 不能为空。

4.    改进

        只需要在mybatis的sqlSessionFactory创建完成,注入SqlSession,调用MapperRefresh启动。

        RootConfiguration.java

package com.xxx.web.common.config;import java.io.IOException;
import java.util.concurrent.Executors;import javax.annotation.PostConstruct;import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Controller;import com.xxx.web.common.config.mybatis.MapperRefresh;@Configuration
@ComponentScan(value = "com.xxx", excludeFilters = { @Filter(Controller.class),@Filter(type = FilterType.ASSIGNABLE_TYPE, value = { RootConfiguration.class }) })
@MapperScan({"com.xxx.web.**.dao"})
public class RootConfiguration extends SpringBootServletInitializer {@Autowiredprivate SqlSession sqlSession;@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder application) {application.registerShutdownHook(false);return application.sources(RootConfiguration.class);}@PostConstructpublic void postConstruct() throws IOException {//Constant.threadPool = Executors.newFixedThreadPool(20);Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/**/*Mapper.xml");new MapperRefresh(resources, sqlSession.getConfiguration()).run();}	
}

启动类:

        MainApplication.java

package com.xxx.web;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;import com.xxx.web.common.config.RootConfiguration;@SpringBootApplication
public class MainApplication {public static void main(String[] args) {SpringApplication.run(RootConfiguration.class, args);}
}

application.yml

spring: thymeleaf:prefix: classpath:/templates/datasource: type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/xxx?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8username: adminpassword: admin@2017initialSize: 1minIdle: 3maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 50000validationQuery: select 'x'testWhileIdle: truetestOnBorrow: falsetestOnReturn: false# 打开PSCache,并且指定每个连接上PSCache的大小poolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙	#,wallfilters: stat,slf4j# 通过connectProperties属性来打开mergeSql功能;慢SQL记录connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000# 合并多个DruidDataSource的监控数据useGlobalDataSourceStat: true
mybatis: configuration:map-underscore-to-camel-case: true#打印日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: mybatis/**/*Mapper.xmltypeAliasesPackage: com.xxx.entity.*
#配置缓存和session存储方式,默认ehcache,可选redis
cacheType: ehcache

5.    多说一句

        关于mybatis-refresh.properties中mappingPath

            指的是src/main/resources的最父级的mybatis文件夹,按下图的话:

mappingPath=mybatis

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412959.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue cli vue 3.x

vue cli & vue 3.x https://cli.vuejs.org/dev-guide/ui-api.html#ui-api https://cli.vuejs.org/zh/guide/#cli vue cli & how to select the option in cmd ? vue cli & 选中 option a select all & i select all 1,2,3,4,5,6,7,8,9,0 分别对应 order 转载…

jenkins svn/git sonarqube scanner 代码集成测试

大家好&#xff0c;我是烤鸭&#xff1a;今天分享一个代码检测工具sonar&#xff0c;在jenkins集成的时候使用。 环境:sonarqube 7.1jenkins 2.12xsonarqube scanner &#xff08;官网最新版3.2.0.1227&#xff09;1. jenkins svn/git 搭建项目https://blog.csdn.net/Angry…

射频与微波测量之S参数

转自&#xff1a;https://www.cnblogs.com/lyh523329053/p/9128577.html S参数 S散射也叫散射参数。是微波传输中的一组重要参数。由于我们很难在高频率时测量电流或电压&#xff0c;因此我们要测量散射参数或 S 参数。这些参数用来表征RF 元件或网络的电气属性或性能&#xff…

JAVA构造对象的几种方式(构建器、构造器)

大家好&#xff0c;我是烤鸭&#xff1a;今天说一下初始化对象的几种方式&#xff1a;1. 多参数构造器2. 构建器3. 构造器后 get/set方法举个例子:这里有个机构entity&#xff0c;提供一个默认构造器 package com.xxx.xxx.modules.sys.entity;/*** 机构Entity* versi…

Django框架(十二)-- Djang与Ajax

一、什么是Ajax AJAX&#xff08;Asynchronous Javascript And XML&#xff09;翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互&#xff0c;传输的数据为XML&#xff08;当然&#xff0c;传输的数据不只是XML,现在更多使用json数据&#xf…

javascript 将table导出 Excel ,可跨行跨列

原文地址&#xff1a;https://www.cnblogs.com/hailexuexi/p/10795887.html <script language"JavaScript" type"text/javascript">//jQuery HTML导出Excel文件(兼容IE及所有浏览器)function HtmlExportToExcel(tableid,file_name) {var filename fi…

wampserver 搭建 php环境 运行方法

大家好&#xff0c;我是烤鸭&#xff1a;今天分享的是如何用wamp 运行 php代码。1. wampserver下载&#xff1a;下载地址&#xff1a;https://sourceforge.net/projects/wampserver/files/WampServer%203/WampServer%203.0.0/Addons/Php/wampserver3_x64_addon_php7.2.7.exe…

java php des加密 byte数组16进制 DESTools

大家好&#xff0c;我是烤鸭:今天分享的是java 和 php des 加密。因为接口对接&#xff0c;难免不同语言&#xff0c;加密又是必不可少的。作为接口的提供方&#xff0c;必须把加密规则写好&#xff0c;最好有不同语言的加密demo。1. java版本的des加密解密工具类DESTools.j…

高可用Eureka注册中心配置说明(双机部署)

目 录 1. 高可用EureKa注册中心示意图 2. Eureka实例相互注册配置 3. 微服务注册到Eureka配置 4. 启动步骤及配置成功检查 5. 说明事项 1. 高可用EureKa注册中心示意图 Spring Cloud的Eureka Server的高可用实际上就是将自己作为服务向其他服注册中心注册自己&#xff0c;形成…

java 实现 常见排序算法(一) 冒泡排序

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下基础排序算法之冒泡排序。 1. 冒泡排序&#xff1a; 原理&#xff1a;比较两个相邻的元素&#xff0c;将较大的元素交换至右端。 思路&#xff1a;依次比较相邻的两个数&#xff0c;将小数放在前面&#xff0c;大…

vue学习之npm

任何一门计算机语言都包含了丰富的第三方库&#xff0c;npm就是JavaScript这门语言的第三方库管理工具&#xff0c;本文详细介绍了JavaScript的包管理工具&#xff0c;npm。 在计算机中安装好Node.js之后&#xff0c;默认已经安装好了npm包管理工具&#xff0c;我们可以输入npm…

Java 深copy 浅copy 引用copy

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下浅copy和深copy。 1. 深copy 什么是深copy&#xff0c;只复制原对象属性值&#xff0c;不管地址。 说一下业务场景&#xff1a; 如果我想创建一个对象&#xff0c;只是对原对象的某个属性值改变。普通的做法就是new 一个…

linux定时任务清理cache缓存

大家好&#xff0c;我是烤鸭&#xff1a; 如果你出现类似cache过多的情况&#xff0c;请参考这篇。 buff/cache 占了1.6G&#xff0c;多数情况下是无所谓的。但是有时候在系统内存不足的时候&#xff0c;可能会影响其他程序的执行。 之前就遇到过 jenkins 因为内存不足 集成失败…

SecureCRT Application 已停止工作

解决方法一&#xff1a; cmd ----> regedit —>HKEY_LOCAL_MACHINE\SOFTWARE\vandyke 删掉vandyke 解决方法二&#xff1a; SecureCRT使用过程中出现异常后自动关闭&#xff0c;导致下次无法正常启动&#xff08;运行程序无反应&#xff09;&#xff0c;此时一种可能的原…

SQLServer之事务简介

事务定义 事务是单个的工作单元。事务是在数据库上按照一定的逻辑顺序执行的任务序列&#xff0c;既可以由用户手动执行&#xff0c;也可以由某种数据库程序自动执行。 事务分类 自动提交事务 每条单独的语句都是一个事务。 在自动提交模式下&#xff0c; 每个数据库操作是在执…

Vmware centos无法连接网络

在vmware下安装了centos7&#xff0c;桥接模式&#xff0c;无法连接网络 解决方案一&#xff1a; 然后重启虚拟机 解决方案二&#xff1a; 然后再试试重启能否联网 解决方案三&#xff1a;

mybatis 一对一 一对多 级联查询

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下关于mybatis的级联查询。 环境&#xff1a; mybatis 3.2.8 spring 4.1.9 1. 业务场景 在一个人申请某些账号或者权限的时候&#xff0c;比如微信的认证流程。 会让你一步一步按要求输入&#xff0c;比如第一步&am…

IDEA启动tomcat报错java.util.zip.ZipException: error in opening zip file

原因CATALINA_BASE参数后面多了一个斜杠 将&#xff1a;CATALINA_BASEK:\Tomcats\apache-tomcat-9.0.10_cform\ 改&#xff1a;CATALINA_BASEK:\Tomcats\apache-tomcat-9.0.10_cform

java 实现 常见排序算法(二) 插入排序

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享一下基础排序算法之直接插入排序。 1. 直接插入排序&#xff1a; 原理&#xff1a;假设前面的数为有序数列&#xff0c;然后有序数列与无序数列的每个数比较&#xff0c;我们可以从右向左比较 思路&#xff1a;从第2…

linux 常用 启动命令 汇总

大家好&#xff0c;我是烤鸭&#xff1a; 没有准备启动脚本。服务器出问题之后&#xff0c;重启就是全部重启。 如&#xff1a; mysql,nginx,tomcat,zookeeper,rabbitmq,mongodb,redis,xxxxx 头大。持续更新。。。 consul启动&#xff1a; (公网可以访问 使用-client 0.0.0…