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 转载…

[css] 如果css文件过大时,如何异步加载它?

[css] 如果css文件过大时&#xff0c;如何异步加载它&#xff1f; 分割成多个CSS文件进行Gzip压缩link preload个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

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…

[css] 你有使用过字体图标吗?它有什么好处?

[css] 你有使用过字体图标吗&#xff1f;它有什么好处&#xff1f; 代替图片&#xff0c;可以减少http请求次数&#xff0c;提高页面加载性能。个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录…

Jqgried树形列表

**************************************Jqgrid树列表***************************************function initGrid_test() {//必要字段&#xff1a;id,name,level,parent,isLeaf,expandedvar topicjson{"response": [{"id": "1", "name&qu…

射频与微波测量之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…

[css] 请说说你对vh、vw的理解以及它们的运用场景是什么?

[css] 请说说你对vh、vw的理解以及它们的运用场景是什么&#xff1f; vw: 100vw为视窗的宽度&#xff0c;即1vw是视窗宽度的1%vh: 100vh为视窗的高度&#xff0c;即1vh是视窗高度的1%运用场景图片查看大图&#xff1a;img { max-height: 90vh; }代替rem实现移动端布局个人简介 …

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…

[css] css怎么更改表单的单选框或下拉框的默认样式?

[css] css怎么更改表单的单选框或下拉框的默认样式&#xff1f; 下拉框select可以通过appearance:none去除默认样式&#xff0c;然后进行自定义&#xff0c;但是option标签不能通过CSS自定义&#xff0c;所以最佳方案是自定义标签重写select单选框隐藏input标签&#xff0c;自定…

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…

Mysql数据库查询当前操作的数据库名

查询数据库名&#xff1a; select database()查询表结构&#xff1a; select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME表名 and table_schema (select database())

redis的学习使用,第二章

在IDEA里面使用redis&#xff0c;使用架包 jedis-2.4.2.jar 1 public class Main {2 3 public static void main(String[] args) {4 Jedis jedis new Jedis("127.0.0.1",6379);5 //string类型6 jedis.set("java","aga…

[css] 你了解css3的currentColor吗?举例说明它的作用是什么?

[css] 你了解css3的currentColor吗&#xff1f;举例说明它的作用是什么&#xff1f; currentColor是 color 属性的值&#xff0c;具体意思是指&#xff1a;currentColor关键字的使用值是 color 属性值的计算值。如果currentColor关键字被应用在 color 属性自身&#xff0c;则相…

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

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

BDD框架之Cucumber研究

BDD框架之Cucumber研究 引用链接&#xff1a;http://kongqingyun123.blog.163.com/blog/static/6377283520134158437813/ Cucumber是BDD&#xff08;行为驱动开发&#xff09;中成熟的一个框架&#xff0c;官方网址: http://cukes.info/1、cucumber安装1、安装ruby2、gem insta…

Oracle修改字段的顺序

一&#xff1a;简单粗暴 1,Oracle: create table CFORM_COULUMN_2 as &#xff08;select 字段A,字段B from CFORM_COULUMN);2,Sqlserver select 字段A,字段B.... into CFORM_COULUMN_2 from CFORM_COULUMN二&#xff1a;通过SYS数据库更新字段顺序 1&#xff0c;查询表…

[css] 怎么去掉点击a链接或者图片出现的边框?

[css] 怎么去掉点击a链接或者图片出现的边框&#xff1f; a{text-decoration:none} img{border:0 none}个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

springboot 2.x tomcat war包部署 localhost-startStop-1 启动卡顿卡死

大家好&#xff0c;我是烤鸭&#xff1a; 关于springboot war包部署tomcat。环境&#xff1a;springboot 2.0.3.RELEASEapache-tomcat-8.5.321. 修改pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns:xsi"http://www.w3.org…