实现MyBatis Mapper XML文件增量动态刷新,自动加载,热加载,热部署

实现MyBatis Mapper XML文件增量动态刷新,自动加载,热加载,热部署

    博客分类:
  • Java
  • MyBatis
阅读更多

    最初启动服务后Mapper XML文件,必须重启服务才能生效,这样就大大影响了我们的开发效率。

    网上同学们也有实现类似功能,但都是全部清空,全部刷新XML,这样硬件消耗比较严重,加载时间也比较长。我们只修改了几行SQL就没有必要全部加载,只需要加载修改的问题就行了。

    后来为了急需解决这个问题,进行修改MyBatis源码实现Mapper XML增量刷新,直接覆盖方式实现,使用classloader的加载机制优先加载,并应用到了jeesite中,但是经过MyBatis几次升级后,不得不需要重新修改,部署也麻烦,入侵性太强。

    周末有幸又重新研究下源代码将刷新部分,分离出来,实现MyBatis Mapper文件动态重新加载,只加载修改的文件,今天分享出来,不多说,看源码,注释很详细: 

Java代码   收藏代码
  1. /** 
  2.  * Copyright (c) 2012-Now https://github.com/thinkgem/jeesite. 
  3.  */  
  4. package com.thinkgem.jeesite.mybatis.thread;  
  5.   
  6. import java.io.File;  
  7. import java.io.FileInputStream;  
  8. import java.io.FileNotFoundException;  
  9. import java.io.InputStream;  
  10. import java.lang.reflect.Field;  
  11. import java.util.ArrayList;  
  12. import java.util.HashMap;  
  13. import java.util.List;  
  14. import java.util.Map;  
  15. import java.util.Properties;  
  16. import java.util.Set;  
  17.   
  18. import org.apache.commons.lang3.StringUtils;  
  19. import org.apache.ibatis.builder.xml.XMLMapperBuilder;  
  20. import org.apache.ibatis.executor.ErrorContext;  
  21. import org.apache.ibatis.session.Configuration;  
  22. import org.apache.log4j.Logger;  
  23. import org.springframework.core.NestedIOException;  
  24. import org.springframework.core.io.Resource;  
  25.   
  26. import com.google.common.collect.Sets;  
  27.   
  28. /** 
  29.  * 刷新MyBatis Mapper XML 线程 
  30.  * @author ThinkGem 
  31.  * @version 2016-5-29 
  32.  */  
  33. public class MapperRefresh implements java.lang.Runnable {  
  34.   
  35.     public static Logger log = Logger.getLogger(MapperRefresh.class);  
  36.   
  37.     private static String filename = "/mybatis-refresh.properties";  
  38.     private static Properties prop = new Properties();  
  39.   
  40.     private static boolean enabled;         // 是否启用Mapper刷新线程功能  
  41.     private static boolean refresh;         // 刷新启用后,是否启动了刷新线程  
  42.       
  43.     private Set location;         // Mapper实际资源路径  
  44.       
  45.     private Resource[] mapperLocations;     // Mapper资源路径  
  46.     private Configuration configuration;        // MyBatis配置对象  
  47.       
  48.     private Long beforeTime = 0L;           // 上一次刷新时间  
  49.     private static int delaySeconds;        // 延迟刷新秒数  
  50.     private static int sleepSeconds;        // 休眠时间  
  51.     private static String mappingPath;      // xml文件夹匹配字符串,需要根据需要修改  
  52.   
  53.     static {  
  54.           
  55.         try {  
  56.             prop.load(MapperRefresh.class.getResourceAsStream(filename));  
  57.         } catch (Exception e) {  
  58.             e.printStackTrace();  
  59.             System.out.println("Load mybatis-refresh “"+filename+"” file error.");  
  60.         }  
  61.   
  62.         enabled = "true".equalsIgnoreCase(getPropString("enabled"));  
  63.           
  64.         delaySeconds = getPropInt("delaySeconds");  
  65.         sleepSeconds = getPropInt("sleepSeconds");  
  66.         mappingPath = getPropString("mappingPath");  
  67.   
  68.         delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;  
  69.         sleepSeconds = sleepSeconds == 0 ? 3 : sleepSeconds;  
  70.         mappingPath = StringUtils.isBlank(mappingPath) ? "mappings" : mappingPath;  
  71.   
  72.         log.debug("[enabled] " + enabled);  
  73.         log.debug("[delaySeconds] " + delaySeconds);  
  74.         log.debug("[sleepSeconds] " + sleepSeconds);  
  75.         log.debug("[mappingPath] " + mappingPath);  
  76.     }  
  77.   
  78.     public static boolean isRefresh() {  
  79.         return refresh;  
  80.     }  
  81.   
  82.     public MapperRefresh(Resource[] mapperLocations, Configuration configuration) {  
  83.         this.mapperLocations = mapperLocations;  
  84.         this.configuration = configuration;  
  85.     }  
  86.   
  87.     @Override  
  88.     public void run() {  
  89.   
  90.         beforeTime = System.currentTimeMillis();  
  91.   
  92.         log.debug("[location] " + location);  
  93.         log.debug("[configuration] " + configuration);  
  94.   
  95.         if (enabled) {  
  96.             // 启动刷新线程  
  97.             final MapperRefresh runnable = this;  
  98.             new Thread(new java.lang.Runnable() {  
  99.                 @Override  
  100.                 public void run() {  
  101.                       
  102.                     if (location == null){  
  103.                         location = Sets.newHashSet();  
  104.                         log.debug("MapperLocation's length:" + mapperLocations.length);  
  105.                         for (Resource mapperLocation : mapperLocations) {  
  106.                             String s = mapperLocation.toString().replaceAll("\\\\", "/");  
  107.                             s = s.substring("file [".length(), s.lastIndexOf(mappingPath) + mappingPath.length());  
  108.                             if (!location.contains(s)) {  
  109.                                 location.add(s);  
  110.                                 log.debug("Location:" + s);  
  111.                             }  
  112.                         }  
  113.                         log.debug("Locarion's size:" + location.size());  
  114.                     }  
  115.   
  116.                     try {  
  117.                         Thread.sleep(delaySeconds * 1000);  
  118.                     } catch (InterruptedException e2) {  
  119.                         e2.printStackTrace();  
  120.                     }  
  121.                     refresh = true;  
  122.   
  123.                     System.out.println("========= Enabled refresh mybatis mapper =========");  
  124.   
  125.                     while (true) {  
  126.                         try {  
  127.                             for (String s : location) {  
  128.                                 runnable.refresh(s, beforeTime);  
  129.                             }  
  130.                         } catch (Exception e1) {  
  131.                             e1.printStackTrace();  
  132.                         }  
  133.                         try {  
  134.                             Thread.sleep(sleepSeconds * 1000);  
  135.                         } catch (InterruptedException e) {  
  136.                             e.printStackTrace();  
  137.                         }  
  138.   
  139.                     }  
  140.                 }  
  141.             }, "MyBatis-Mapper-Refresh").start();  
  142.         }  
  143.     }  
  144.   
  145.     /** 
  146.      * 执行刷新 
  147.      * @param filePath 刷新目录 
  148.      * @param beforeTime 上次刷新时间 
  149.      * @throws NestedIOException 解析异常 
  150.      * @throws FileNotFoundException 文件未找到 
  151.      * @author ThinkGem 
  152.      */  
  153.     @SuppressWarnings({ "rawtypes""unchecked" })  
  154.     private void refresh(String filePath, Long beforeTime) throws Exception {  
  155.   
  156.         // 本次刷新时间  
  157.         Long refrehTime = System.currentTimeMillis();  
  158.   
  159.         // 获取需要刷新的Mapper文件列表  
  160.         List fileList = this.getRefreshFile(new File(filePath), beforeTime);  
  161.         if (fileList.size() > 0) {  
  162.             log.debug("Refresh file: " + fileList.size());  
  163.         }  
  164.         for (int i = 0; i < fileList.size(); i++) {  
  165.             InputStream inputStream = new FileInputStream(fileList.get(i));  
  166.             String resource = fileList.get(i).getAbsolutePath();  
  167.             try {  
  168.                   
  169.                 // 清理原有资源,更新为自己的StrictMap方便,增量重新加载  
  170.                 String[] mapFieldNames = new String[]{  
  171.                     "mappedStatements""caches",  
  172.                     "resultMaps""parameterMaps",  
  173.                     "keyGenerators""sqlFragments"  
  174.                 };  
  175.                 for (String fieldName : mapFieldNames){  
  176.                     Field field = configuration.getClass().getDeclaredField(fieldName);  
  177.                     field.setAccessible(true);  
  178.                     Map map = ((Map)field.get(configuration));  
  179.                     if (!(map instanceof StrictMap)){  
  180.                         Map newMap = new StrictMap(StringUtils.capitalize(fieldName) + "collection");  
  181.                         for (Object key : map.keySet()){  
  182.                             try {  
  183.                                 newMap.put(key, map.get(key));  
  184.                             }catch(IllegalArgumentException ex){  
  185.                                 newMap.put(key, ex.getMessage());  
  186.                             }  
  187.                         }  
  188.                         field.set(configuration, newMap);  
  189.                     }  
  190.                 }  
  191.                   
  192.                 // 清理已加载的资源标识,方便让它重新加载。  
  193.                 Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");  
  194.                 loadedResourcesField.setAccessible(true);  
  195.                 Set loadedResourcesSet = ((Set)loadedResourcesField.get(configuration));  
  196.                 loadedResourcesSet.remove(resource);  
  197.                   
  198.                 //重新编译加载资源文件。  
  199.                 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, configuration,   
  200.                         resource, configuration.getSqlFragments());  
  201.                 xmlMapperBuilder.parse();  
  202.             } catch (Exception e) {  
  203.                 throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);  
  204.             } finally {  
  205.                 ErrorContext.instance().reset();  
  206.             }  
  207.             System.out.println("Refresh file: " + mappingPath + StringUtils.substringAfterLast(fileList.get(i).getAbsolutePath(), mappingPath));  
  208.             if (log.isDebugEnabled()) {  
  209.                 log.debug("Refresh file: " + fileList.get(i).getAbsolutePath());  
  210.                 log.debug("Refresh filename: " + fileList.get(i).getName());  
  211.             }  
  212.         }  
  213.         // 如果刷新了文件,则修改刷新时间,否则不修改  
  214.         if (fileList.size() > 0) {  
  215.             this.beforeTime = refrehTime;  
  216.         }  
  217.     }  
  218.       
  219.     /** 
  220.      * 获取需要刷新的文件列表 
  221.      * @param dir 目录 
  222.      * @param beforeTime 上次刷新时间 
  223.      * @return 刷新文件列表 
  224.      */  
  225.     private List getRefreshFile(File dir, Long beforeTime) {  
  226.         List fileList = new ArrayList();  
  227.   
  228.         File[] files = dir.listFiles();  
  229.         if (files != null) {  
  230.             for (int i = 0; i < files.length; i++) {  
  231.                 File file = files[i];  
  232.                 if (file.isDirectory()) {  
  233.                     fileList.addAll(this.getRefreshFile(file, beforeTime));  
  234.                 } else if (file.isFile()) {  
  235.                     if (this.checkFile(file, beforeTime)) {  
  236.                         fileList.add(file);  
  237.                     }  
  238.                 } else {  
  239.                     System.out.println("Error file." + file.getName());  
  240.                 }  
  241.             }  
  242.         }  
  243.         return fileList;  
  244.     }  
  245.   
  246.     /** 
  247.      * 判断文件是否需要刷新 
  248.      * @param file 文件 
  249.      * @param beforeTime 上次刷新时间 
  250.      * @return 需要刷新返回true,否则返回false 
  251.      */  
  252.     private boolean checkFile(File file, Long beforeTime) {  
  253.         if (file.lastModified() > beforeTime) {  
  254.             return true;  
  255.         }  
  256.         return false;  
  257.     }  
  258.   
  259.     /** 
  260.      * 获取整数属性 
  261.      * @param key 
  262.      * @return 
  263.      */  
  264.     private static int getPropInt(String key) {  
  265.         int i = 0;  
  266.         try {  
  267.             i = Integer.parseInt(getPropString(key));  
  268.         } catch (Exception e) {  
  269.         }  
  270.         return i;  
  271.     }  
  272.   
  273.     /** 
  274.      * 获取字符串属性 
  275.      * @param key 
  276.      * @return 
  277.      */  
  278.     private static String getPropString(String key) {  
  279.         return prop == null ? null : prop.getProperty(key);  
  280.     }  
  281.   
  282.     /** 
  283.      * 重写 org.apache.ibatis.session.Configuration.StrictMap 类 
  284.      * 来自 MyBatis3.4.0版本,修改 put 方法,允许反复 put更新。 
  285.      */  
  286.     public static class StrictMap extends HashMap {  
  287.   
  288.         private static final long serialVersionUID = -4950446264854982944L;  
  289.         private String name;  
  290.   
  291.         public StrictMap(String name, int initialCapacity, float loadFactor) {  
  292.             super(initialCapacity, loadFactor);  
  293.             this.name = name;  
  294.         }  
  295.   
  296.         public StrictMap(String name, int initialCapacity) {  
  297.             super(initialCapacity);  
  298.             this.name = name;  
  299.         }  
  300.   
  301.         public StrictMap(String name) {  
  302.             super();  
  303.             this.name = name;  
  304.         }  
  305.   
  306.         public StrictMap(String name, Mapextends V> m) {  
  307.             super(m);  
  308.             this.name = name;  
  309.         }  
  310.   
  311.         @SuppressWarnings("unchecked")  
  312.         public V put(String key, V value) {  
  313.             // ThinkGem 如果现在状态为刷新,则刷新(先删除后添加)  
  314.             if (MapperRefresh.isRefresh()) {  
  315.                 remove(key);  
  316.                 MapperRefresh.log.debug("refresh key:" + key.substring(key.lastIndexOf(".") + 1));  
  317.             }  
  318.             // ThinkGem end  
  319.             if (containsKey(key)) {  
  320.                 throw new IllegalArgumentException(name + " already contains value for " + key);  
  321.             }  
  322.             if (key.contains(".")) {  
  323.                 final String shortKey = getShortName(key);  
  324.                 if (super.get(shortKey) == null) {  
  325.                     super.put(shortKey, value);  
  326.                 } else {  
  327.                     super.put(shortKey, (V) new Ambiguity(shortKey));  
  328.                 }  
  329.             }  
  330.             return super.put(key, value);  
  331.         }  
  332.   
  333.         public V get(Object key) {  
  334.             V value = super.get(key);  
  335.             if (value == null) {  
  336.                 throw new IllegalArgumentException(name + " does not contain value for " + key);  
  337.             }  
  338.             if (value instanceof Ambiguity) {  
  339.                 throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name  
  340.                         + " (try using the full name including the namespace, or rename one of the entries)");  
  341.             }  
  342.             return value;  
  343.         }  
  344.   
  345.         private String getShortName(String key) {  
  346.             final String[] keyparts = key.split("\\.");  
  347.             return keyparts[keyparts.length - 1];  
  348.         }  
  349.   
  350.         protected static class Ambiguity {  
  351.             private String subject;  
  352.   
  353.             public Ambiguity(String subject) {  
  354.                 this.subject = subject;  
  355.             }  
  356.   
  357.             public String getSubject() {  
  358.                 return subject;  
  359.             }  
  360.         }  
  361.     }  
  362. }  
/**
 * Copyright (c) 2012-Now https://github.com/thinkgem/jeesite.
 */
package com.thinkgem.jeesite.mybatis.thread;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Field;
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.lang3.StringUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.log4j.Logger;
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 = Logger.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 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.");
     }
    
     enabled = "true".equalsIgnoreCase(getPropString("enabled"));
     
     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 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 getRefreshFile(File dir, Long beforeTime) {
      List fileList = new ArrayList();

      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);
      }

    /**

    • 重写 org.apache.ibatis.session.Configuration.StrictMap 类

    • 来自 MyBatis3.4.0版本,修改 put 方法,允许反复 put更新。
      */
      public static class StrictMap extends HashMap {

      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 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;
       }
      

      }
      }
      }

 

 MyBatis有几个不太好的地方,是当实体类别名重名的时候,Mapper XML有错误的时候,系统启动时会一直等待无法正常启动(其实是加载失败后又重新加载,进入了死循环),这里我也顺便重写下SqlSessionFactoryBean.java文件,解决这个问题,在这个文件里也加入启动上面写的线程类:

 

 1、修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动。

 2、MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动。

 3、加入启动MapperRefresh.java线程服务。 

Java代码   收藏代码
  1. /** 
  2.  *    Copyright 2010-2015 the original author or authors. 
  3.  * 
  4.  *    Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  *    you may not use this file except in compliance with the License. 
  6.  *    You may obtain a copy of the License at 
  7.  * 
  8.  *       http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  *    Unless required by applicable law or agreed to in writing, software 
  11.  *    distributed under the License is distributed on an "AS IS" BASIS, 
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  *    See the License for the specific language governing permissions and 
  14.  *    limitations under the License. 
  15.  */  
  16. package com.thinkgem.jeesite.mybatis.spring;  
  17.   
  18. import static org.springframework.util.Assert.notNull;  
  19. import static org.springframework.util.ObjectUtils.isEmpty;  
  20. import static org.springframework.util.StringUtils.hasLength;  
  21. import static org.springframework.util.StringUtils.tokenizeToStringArray;  
  22.   
  23. import java.io.IOException;  
  24. import java.sql.SQLException;  
  25. import java.util.Properties;  
  26.   
  27. import javax.sql.DataSource;  
  28.   
  29. import org.apache.ibatis.builder.xml.XMLConfigBuilder;  
  30. import org.apache.ibatis.builder.xml.XMLMapperBuilder;  
  31. import org.apache.ibatis.executor.ErrorContext;  
  32. import org.apache.ibatis.logging.Log;  
  33. import org.apache.ibatis.logging.LogFactory;  
  34. import org.apache.ibatis.mapping.DatabaseIdProvider;  
  35. import org.apache.ibatis.mapping.Environment;  
  36. import org.apache.ibatis.plugin.Interceptor;  
  37. import org.apache.ibatis.reflection.factory.ObjectFactory;  
  38. import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;  
  39. import org.apache.ibatis.session.Configuration;  
  40. import org.apache.ibatis.session.SqlSessionFactory;  
  41. import org.apache.ibatis.session.SqlSessionFactoryBuilder;  
  42. import org.apache.ibatis.transaction.TransactionFactory;  
  43. import org.apache.ibatis.type.TypeHandler;  
  44. import org.mybatis.spring.transaction.SpringManagedTransactionFactory;  
  45. import org.springframework.beans.factory.FactoryBean;  
  46. import org.springframework.beans.factory.InitializingBean;  
  47. import org.springframework.context.ApplicationEvent;  
  48. import org.springframework.context.ApplicationListener;  
  49. import org.springframework.context.ConfigurableApplicationContext;  
  50. import org.springframework.context.event.ContextRefreshedEvent;  
  51. import org.springframework.core.NestedIOException;  
  52. import org.springframework.core.io.Resource;  
  53. import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;  
  54.   
  55. import com.thinkgem.jeesite.common.mybatis.thread.MapperRefresh;  
  56.   
  57. /** 
  58.  * {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}. 
  59.  * This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context; 
  60.  * the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection. 
  61.  * 
  62.  * Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction 
  63.  * demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for transactions 
  64.  * which span multiple databases or when container managed transactions (CMT) are being used. 
  65.  * 
  66.  * @author Putthibong Boonbong 
  67.  * @author Hunter Presnall 
  68.  * @author Eduardo Macarron 
  69.  *  
  70.  * @see #setConfigLocation 
  71.  * @see #setDataSource 
  72.  * @version $Id$ 
  73.  * @modify ThinkGem 2016-5-24 来自 MyBatisSpring1.2.3版本 
  74.  */  
  75. public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {  
  76.   
  77.   private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);  
  78.   
  79.   private Resource configLocation;  
  80.   
  81.   private Resource[] mapperLocations;  
  82.   
  83.   private DataSource dataSource;  
  84.   
  85.   private TransactionFactory transactionFactory;  
  86.   
  87.   private Properties configurationProperties;  
  88.   
  89.   private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();  
  90.   
  91.   private SqlSessionFactory sqlSessionFactory;  
  92.   
  93.   //EnvironmentAware requires spring 3.1  
  94.   private String environment = SqlSessionFactoryBean.class.getSimpleName();  
  95.   
  96.   private boolean failFast;  
  97.   
  98.   private Interceptor[] plugins;  
  99.   
  100.   private TypeHandler[] typeHandlers;  
  101.   
  102.   private String typeHandlersPackage;  
  103.   
  104.   private Class[] typeAliases;  
  105.   
  106.   private String typeAliasesPackage;  
  107.   
  108.   private Class typeAliasesSuperType;  
  109.   
  110.   //issue #19. No default provider.  
  111.   private DatabaseIdProvider databaseIdProvider;  
  112.   
  113.   private ObjectFactory objectFactory;  
  114.   
  115.   private ObjectWrapperFactory objectWrapperFactory;  
  116.   
  117.   /** 
  118.    * Sets the ObjectFactory. 
  119.    *  
  120.    * @since 1.1.2 
  121.    * @param objectFactory 
  122.    */  
  123.   public void setObjectFactory(ObjectFactory objectFactory) {  
  124.     this.objectFactory = objectFactory;  
  125.   }  
  126.   
  127.   /** 
  128.    * Sets the ObjectWrapperFactory. 
  129.    *  
  130.    * @since 1.1.2 
  131.    * @param objectWrapperFactory 
  132.    */  
  133.   public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {  
  134.     this.objectWrapperFactory = objectWrapperFactory;  
  135.   }  
  136.   
  137.   /** 
  138.    * Gets the DatabaseIdProvider 
  139.    * 
  140.    * @since 1.1.0 
  141.    * @return 
  142.    */  
  143.   public DatabaseIdProvider getDatabaseIdProvider() {  
  144.     return databaseIdProvider;  
  145.   }  
  146.   
  147.   /** 
  148.    * Sets the DatabaseIdProvider. 
  149.    * As of version 1.2.2 this variable is not initialized by default.  
  150.    * 
  151.    * @since 1.1.0 
  152.    * @param databaseIdProvider 
  153.    */  
  154.   public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {  
  155.     this.databaseIdProvider = databaseIdProvider;  
  156.   }  
  157.   
  158.   /** 
  159.    * Mybatis plugin list. 
  160.    * 
  161.    * @since 1.0.1 
  162.    * 
  163.    * @param plugins list of plugins 
  164.    * 
  165.    */  
  166.   public void setPlugins(Interceptor[] plugins) {  
  167.     this.plugins = plugins;  
  168.   }  
  169.   
  170.   /** 
  171.    * Packages to search for type aliases. 
  172.    * 
  173.    * @since 1.0.1 
  174.    * 
  175.    * @param typeAliasesPackage package to scan for domain objects 
  176.    * 
  177.    */  
  178.   public void setTypeAliasesPackage(String typeAliasesPackage) {  
  179.     this.typeAliasesPackage = typeAliasesPackage;  
  180.   }  
  181.   
  182.   /** 
  183.    * Super class which domain objects have to extend to have a type alias created. 
  184.    * No effect if there is no package to scan configured. 
  185.    * 
  186.    * @since 1.1.2 
  187.    * 
  188.    * @param typeAliasesSuperType super class for domain objects 
  189.    * 
  190.    */  
  191.   public void setTypeAliasesSuperType(Class typeAliasesSuperType) {  
  192.     this.typeAliasesSuperType = typeAliasesSuperType;  
  193.   }  
  194.   
  195.   /** 
  196.    * Packages to search for type handlers. 
  197.    * 
  198.    * @since 1.0.1 
  199.    * 
  200.    * @param typeHandlersPackage package to scan for type handlers 
  201.    * 
  202.    */  
  203.   public void setTypeHandlersPackage(String typeHandlersPackage) {  
  204.     this.typeHandlersPackage = typeHandlersPackage;  
  205.   }  
  206.   
  207.   /** 
  208.    * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes} 
  209.    * 
  210.    * @since 1.0.1 
  211.    * 
  212.    * @param typeHandlers Type handler list 
  213.    */  
  214.   public void setTypeHandlers(TypeHandler[] typeHandlers) {  
  215.     this.typeHandlers = typeHandlers;  
  216.   }  
  217.   
  218.   /** 
  219.    * List of type aliases to register. They can be annotated with {@code Alias} 
  220.    * 
  221.    * @since 1.0.1 
  222.    * 
  223.    * @param typeAliases Type aliases list 
  224.    */  
  225.   public void setTypeAliases(Class[] typeAliases) {  
  226.     this.typeAliases = typeAliases;  
  227.   }  
  228.   
  229.   /** 
  230.    * If true, a final check is done on Configuration to assure that all mapped 
  231.    * statements are fully loaded and there is no one still pending to resolve 
  232.    * includes. Defaults to false. 
  233.    * 
  234.    * @since 1.0.1 
  235.    * 
  236.    * @param failFast enable failFast 
  237.    */  
  238.   public void setFailFast(boolean failFast) {  
  239.     this.failFast = failFast;  
  240.   }  
  241.   
  242.   /** 
  243.    * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is 
  244.    * "WEB-INF/mybatis-configuration.xml". 
  245.    */  
  246.   public void setConfigLocation(Resource configLocation) {  
  247.     this.configLocation = configLocation;  
  248.   }  
  249.   
  250.   /** 
  251.    * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} 
  252.    * configuration at runtime. 
  253.    * 
  254.    * This is an alternative to specifying "<sqlmapper>" entries in an MyBatis config file. 
  255.    * This property being based on Spring's resource abstraction also allows for specifying 
  256.    * resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml". 
  257.    */  
  258.   public void setMapperLocations(Resource[] mapperLocations) {  
  259.     this.mapperLocations = mapperLocations;  
  260.   }  
  261.   
  262.   /** 
  263.    * Set optional properties to be passed into the SqlSession configuration, as alternative to a 
  264.    * {@code <properties>} tag in the configuration xml file. This will be used to 
  265.    * resolve placeholders in the config file. 
  266.    */  
  267.   public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {  
  268.     this.configurationProperties = sqlSessionFactoryProperties;  
  269.   }  
  270.   
  271.   /** 
  272.    * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} 
  273.    * should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same 
  274.    * JNDI DataSource for both. 
  275.    * 
  276.    * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code 
  277.    * accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}. 
  278.    * 
  279.    * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not 
  280.    * a {@code TransactionAwareDataSourceProxy}. Only data access code may work with 
  281.    * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the 
  282.    * underlying target {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} 
  283.    * passed in, it will be unwrapped to extract its target {@code DataSource}. 
  284.    * 
  285.    */  
  286.   public void setDataSource(DataSource dataSource) {  
  287.     if (dataSource instanceof TransactionAwareDataSourceProxy) {  
  288.       // If we got a TransactionAwareDataSourceProxy, we need to perform  
  289.       // transactions for its underlying target DataSource, else data  
  290.       // access code won't see properly exposed transactions (i.e.  
  291.       // transactions for the target DataSource).  
  292.       this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();  
  293.     } else {  
  294.       this.dataSource = dataSource;  
  295.     }  
  296.   }  
  297.   
  298.   /** 
  299.    * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}. 
  300.    * 
  301.    * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By 
  302.    * default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances. 
  303.    * 
  304.    */  
  305.   public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {  
  306.     this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;  
  307.   }  
  308.   
  309.   /** 
  310.    * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory} 
  311.    * 
  312.    * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases: 
  313.    * be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction, 
  314.    * SqlSession operations will execute SQL statements non-transactionally. 
  315.    * 
  316.    * It is strongly recommended to use the default {@code TransactionFactory}. If not used, any 
  317.    * attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if 
  318.    * a transaction is active. 
  319.    * 
  320.    * @see SpringManagedTransactionFactory 
  321.    * @param transactionFactory the MyBatis TransactionFactory 
  322.    */  
  323.   public void setTransactionFactory(TransactionFactory transactionFactory) {  
  324.     this.transactionFactory = transactionFactory;  
  325.   }  
  326.   
  327.   /** 
  328.    * NOTE: This class overrides any {@code Environment} you have set in the MyBatis 
  329.    * config file. This is used only as a placeholder name. The default value is 
  330.    * {@code SqlSessionFactoryBean.class.getSimpleName()}. 
  331.    * 
  332.    * @param environment the environment name 
  333.    */  
  334.   public void setEnvironment(String environment) {  
  335.     this.environment = environment;  
  336.   }  
  337.   
  338.   /** 
  339.    * {@inheritDoc} 
  340.    */  
  341.   @Override  
  342.   public void afterPropertiesSet() throws Exception {  
  343.     notNull(dataSource, "Property 'dataSource' is required");  
  344.     notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");  
  345.   
  346.     this.sqlSessionFactory = buildSqlSessionFactory();  
  347.   }  
  348.   
  349.   /** 
  350.    * Build a {@code SqlSessionFactory} instance. 
  351.    * 
  352.    * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a 
  353.    * {@code SqlSessionFactory} instance based on an Reader. 
  354.    * 
  355.    * @return SqlSessionFactory 
  356.    * @throws IOException if loading the config file failed 
  357.    */  
  358.   protected SqlSessionFactory buildSqlSessionFactory() throws IOException {  
  359.   
  360.     Configuration configuration;  
  361.   
  362.     XMLConfigBuilder xmlConfigBuilder = null;  
  363.     if (this.configLocation != null) {  
  364.       xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), nullthis.configurationProperties);  
  365.       configuration = xmlConfigBuilder.getConfiguration();  
  366.     } else {  
  367.       if (LOGGER.isDebugEnabled()) {  
  368.         LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");  
  369.       }  
  370.       configuration = new Configuration();  
  371.       configuration.setVariables(this.configurationProperties);  
  372.     }  
  373.   
  374.     if (this.objectFactory != null) {  
  375.       configuration.setObjectFactory(this.objectFactory);  
  376.     }  
  377.   
  378.     if (this.objectWrapperFactory != null) {  
  379.       configuration.setObjectWrapperFactory(this.objectWrapperFactory);  
  380.     }  
  381.   
  382.     if (hasLength(this.typeAliasesPackage)) {  
  383.       String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,  
  384.           ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
  385.       for (String packageToScan : typeAliasPackageArray) {  
  386.         // ThinkGem 修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动  
  387.         try {  
  388.             configuration.getTypeAliasRegistry().registerAliases(packageToScan,  
  389.                     typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);  
  390.         } catch (Exception ex) {  
  391.             LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);  
  392.             throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);  
  393.         } finally {  
  394.             ErrorContext.instance().reset();  
  395.         }  
  396.         // ThinkGem end  
  397.         if (LOGGER.isDebugEnabled()) {  
  398.           LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");  
  399.         }  
  400.       }  
  401.     }  
  402.   
  403.     if (!isEmpty(this.typeAliases)) {  
  404.       for (Class typeAlias : this.typeAliases) {  
  405.         configuration.getTypeAliasRegistry().registerAlias(typeAlias);  
  406.         if (LOGGER.isDebugEnabled()) {  
  407.           LOGGER.debug("Registered type alias: '" + typeAlias + "'");  
  408.         }  
  409.       }  
  410.     }  
  411.   
  412.     if (!isEmpty(this.plugins)) {  
  413.       for (Interceptor plugin : this.plugins) {  
  414.         configuration.addInterceptor(plugin);  
  415.         if (LOGGER.isDebugEnabled()) {  
  416.           LOGGER.debug("Registered plugin: '" + plugin + "'");  
  417.         }  
  418.       }  
  419.     }  
  420.   
  421.     if (hasLength(this.typeHandlersPackage)) {  
  422.       String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,  
  423.           ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
  424.       for (String packageToScan : typeHandlersPackageArray) {  
  425.         configuration.getTypeHandlerRegistry().register(packageToScan);  
  426.         if (LOGGER.isDebugEnabled()) {  
  427.           LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");  
  428.         }  
  429.       }  
  430.     }  
  431.   
  432.     if (!isEmpty(this.typeHandlers)) {  
  433.       for (TypeHandler typeHandler : this.typeHandlers) {  
  434.         configuration.getTypeHandlerRegistry().register(typeHandler);  
  435.         if (LOGGER.isDebugEnabled()) {  
  436.           LOGGER.debug("Registered type handler: '" + typeHandler + "'");  
  437.         }  
  438.       }  
  439.     }  
  440.   
  441.     if (xmlConfigBuilder != null) {  
  442.       try {  
  443.         xmlConfigBuilder.parse();  
  444.   
  445.         if (LOGGER.isDebugEnabled()) {  
  446.           LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");  
  447.         }  
  448.       } catch (Exception ex) {  
  449.         throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);  
  450.       } finally {  
  451.         ErrorContext.instance().reset();  
  452.       }  
  453.     }  
  454.   
  455.     if (this.transactionFactory == null) {  
  456.       this.transactionFactory = new SpringManagedTransactionFactory();  
  457.     }  
  458.   
  459.     configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));  
  460.   
  461.     if (this.databaseIdProvider != null) {  
  462.       try {  
  463.         configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));  
  464.       } catch (SQLException e) {  
  465.         throw new NestedIOException("Failed getting a databaseId", e);  
  466.       }  
  467.     }  
  468.   
  469.     if (!isEmpty(this.mapperLocations)) {  
  470.       for (Resource mapperLocation : this.mapperLocations) {  
  471.         if (mapperLocation == null) {  
  472.           continue;  
  473.         }  
  474.   
  475.         try {  
  476.           XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),  
  477.               configuration, mapperLocation.toString(), configuration.getSqlFragments());  
  478.           xmlMapperBuilder.parse();  
  479.         } catch (Exception e) {  
  480.             // ThinkGem MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动  
  481.             LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
  482.             throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
  483.         } finally {  
  484.           ErrorContext.instance().reset();  
  485.         }  
  486.   
  487.         if (LOGGER.isDebugEnabled()) {  
  488.           LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");  
  489.         }  
  490.       }  
  491.         
  492.       // ThinkGem 启动刷新MapperXML定时器(有助于开发者调试)。  
  493.       new MapperRefresh(this.mapperLocations, configuration).run();  
  494.         
  495.     } else {  
  496.       if (LOGGER.isDebugEnabled()) {  
  497.         LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");  
  498.       }  
  499.     }  
  500.   
  501.     return this.sqlSessionFactoryBuilder.build(configuration);  
  502.   }  
  503.   
  504.   /** 
  505.    * {@inheritDoc} 
  506.    */  
  507.   @Override  
  508.   public SqlSessionFactory getObject() throws Exception {  
  509.     if (this.sqlSessionFactory == null) {  
  510.       afterPropertiesSet();  
  511.     }  
  512.   
  513.     return this.sqlSessionFactory;  
  514.   }  
  515.   
  516.   /** 
  517.    * {@inheritDoc} 
  518.    */  
  519.   @Override  
  520.   public Classextends SqlSessionFactory> getObjectType() {  
  521.     return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();  
  522.   }  
  523.   
  524.   /** 
  525.    * {@inheritDoc} 
  526.    */  
  527.   @Override  
  528.   public boolean isSingleton() {  
  529.     return true;  
  530.   }  
  531.   
  532.   /** 
  533.    * {@inheritDoc} 
  534.    */  
  535.   @Override  
  536.   public void onApplicationEvent(ApplicationEvent event) {  
  537.     if (failFast && event instanceof ContextRefreshedEvent) {  
  538.       // fail-fast -> check all statements are completed  
  539.       this.sqlSessionFactory.getConfiguration().getMappedStatementNames();  
  540.     }  
  541.   }  
  542.   
  543. }   
/**
 *    Copyright 2010-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package com.thinkgem.jeesite.mybatis.spring;

import static org.springframework.util.Assert.notNull;
import static org.springframework.util.ObjectUtils.isEmpty;
import static org.springframework.util.StringUtils.hasLength;
import static org.springframework.util.StringUtils.tokenizeToStringArray;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;

import com.thinkgem.jeesite.common.mybatis.thread.MapperRefresh;

/**

  • {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}.
  • This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context;
  • the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection.
  • Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction
  • demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for transactions
  • which span multiple databases or when container managed transactions (CMT) are being used.
  • @author Putthibong Boonbong
  • @author Hunter Presnall
  • @author Eduardo Macarron
  • @see #setConfigLocation
  • @see #setDataSource
  • @version I d Id Id
  • @modify ThinkGem 2016-5-24 来自 MyBatisSpring1.2.3版本
    */
    public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {

private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);

private Resource configLocation;

private Resource[] mapperLocations;

private DataSource dataSource;

private TransactionFactory transactionFactory;

private Properties configurationProperties;

private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

private SqlSessionFactory sqlSessionFactory;

//EnvironmentAware requires spring 3.1
private String environment = SqlSessionFactoryBean.class.getSimpleName();

private boolean failFast;

private Interceptor[] plugins;

private TypeHandler[] typeHandlers;

private String typeHandlersPackage;

private Class[] typeAliases;

private String typeAliasesPackage;

private Class typeAliasesSuperType;

//issue #19. No default provider.
private DatabaseIdProvider databaseIdProvider;

private ObjectFactory objectFactory;

private ObjectWrapperFactory objectWrapperFactory;

/**

  • Sets the ObjectFactory.
  • @since 1.1.2
  • @param objectFactory
    */
    public void setObjectFactory(ObjectFactory objectFactory) {
    this.objectFactory = objectFactory;
    }

/**

  • Sets the ObjectWrapperFactory.
  • @since 1.1.2
  • @param objectWrapperFactory
    */
    public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
    this.objectWrapperFactory = objectWrapperFactory;
    }

/**

  • Gets the DatabaseIdProvider
  • @since 1.1.0
  • @return
    */
    public DatabaseIdProvider getDatabaseIdProvider() {
    return databaseIdProvider;
    }

/**

  • Sets the DatabaseIdProvider.
  • As of version 1.2.2 this variable is not initialized by default.
  • @since 1.1.0
  • @param databaseIdProvider
    */
    public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
    this.databaseIdProvider = databaseIdProvider;
    }

/**

  • Mybatis plugin list.
  • @since 1.0.1
  • @param plugins list of plugins

*/
public void setPlugins(Interceptor[] plugins) {
this.plugins = plugins;
}

/**

  • Packages to search for type aliases.
  • @since 1.0.1
  • @param typeAliasesPackage package to scan for domain objects

*/
public void setTypeAliasesPackage(String typeAliasesPackage) {
this.typeAliasesPackage = typeAliasesPackage;
}

/**

  • Super class which domain objects have to extend to have a type alias created.
  • No effect if there is no package to scan configured.
  • @since 1.1.2
  • @param typeAliasesSuperType super class for domain objects

*/
public void setTypeAliasesSuperType(Class typeAliasesSuperType) {
this.typeAliasesSuperType = typeAliasesSuperType;
}

/**

  • Packages to search for type handlers.
  • @since 1.0.1
  • @param typeHandlersPackage package to scan for type handlers

*/
public void setTypeHandlersPackage(String typeHandlersPackage) {
this.typeHandlersPackage = typeHandlersPackage;
}

/**

  • Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
  • @since 1.0.1
  • @param typeHandlers Type handler list
    */
    public void setTypeHandlers(TypeHandler[] typeHandlers) {
    this.typeHandlers = typeHandlers;
    }

/**

  • List of type aliases to register. They can be annotated with {@code Alias}
  • @since 1.0.1
  • @param typeAliases Type aliases list
    */
    public void setTypeAliases(Class[] typeAliases) {
    this.typeAliases = typeAliases;
    }

/**

  • If true, a final check is done on Configuration to assure that all mapped
  • statements are fully loaded and there is no one still pending to resolve
  • includes. Defaults to false.
  • @since 1.0.1
  • @param failFast enable failFast
    */
    public void setFailFast(boolean failFast) {
    this.failFast = failFast;
    }

/**

  • Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
  • “WEB-INF/mybatis-configuration.xml”.
    */
    public void setConfigLocation(Resource configLocation) {
    this.configLocation = configLocation;
    }

/**

  • Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}
  • configuration at runtime.
  • This is an alternative to specifying “<sqlmapper>” entries in an MyBatis config file.
  • This property being based on Spring’s resource abstraction also allows for specifying
  • resource patterns here: e.g. “classpath*:sqlmap/*-mapper.xml”.
    */
    public void setMapperLocations(Resource[] mapperLocations) {
    this.mapperLocations = mapperLocations;
    }

/**

  • Set optional properties to be passed into the SqlSession configuration, as alternative to a
  • {@code <properties>} tag in the configuration xml file. This will be used to
  • resolve placeholders in the config file.
    */
    public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
    this.configurationProperties = sqlSessionFactoryProperties;
    }

/**

  • Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource}
  • should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same
  • JNDI DataSource for both.
  • A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code
  • accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.
  • The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not
  • a {@code TransactionAwareDataSourceProxy}. Only data access code may work with
  • {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the
  • underlying target {@code DataSource}. If there’s nevertheless a {@code TransactionAwareDataSourceProxy}
  • passed in, it will be unwrapped to extract its target {@code DataSource}.

*/
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
// If we got a TransactionAwareDataSourceProxy, we need to perform
// transactions for its underlying target DataSource, else data
// access code won’t see properly exposed transactions (i.e.
// transactions for the target DataSource).
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
} else {
this.dataSource = dataSource;
}
}

/**

  • Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}.
  • This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By
  • default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances.

*/
public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
}

/**

  • Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}
  • The default {@code SpringManagedTransactionFactory} should be appropriate for all cases:
  • be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction,
  • SqlSession operations will execute SQL statements non-transactionally.
  • It is strongly recommended to use the default {@code TransactionFactory}. If not used, any
  • attempt at getting an SqlSession through Spring’s MyBatis framework will throw an exception if
  • a transaction is active.
  • @see SpringManagedTransactionFactory
  • @param transactionFactory the MyBatis TransactionFactory
    */
    public void setTransactionFactory(TransactionFactory transactionFactory) {
    this.transactionFactory = transactionFactory;
    }

/**

  • NOTE: This class overrides any {@code Environment} you have set in the MyBatis
  • config file. This is used only as a placeholder name. The default value is
  • {@code SqlSessionFactoryBean.class.getSimpleName()}.
  • @param environment the environment name
    */
    public void setEnvironment(String environment) {
    this.environment = environment;
    }

/**

  • {@inheritDoc}
    */
    @Override
    public void afterPropertiesSet() throws Exception {
    notNull(dataSource, “Property ‘dataSource’ is required”);
    notNull(sqlSessionFactoryBuilder, “Property ‘sqlSessionFactoryBuilder’ is required”);
this.sqlSessionFactory = buildSqlSessionFactory();

}

/**

  • Build a {@code SqlSessionFactory} instance.
  • The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
  • {@code SqlSessionFactory} instance based on an Reader.
  • @return SqlSessionFactory
  • @throws IOException if loading the config file failed
    */
    protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;

XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
  xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  configuration = xmlConfigBuilder.getConfiguration();
} else {
  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
  }
  configuration = new Configuration();
  configuration.setVariables(this.configurationProperties);
}

if (this.objectFactory != null) {
  configuration.setObjectFactory(this.objectFactory);
}

if (this.objectWrapperFactory != null) {
  configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}

if (hasLength(this.typeAliasesPackage)) {
  String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
      ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  for (String packageToScan : typeAliasPackageArray) {
	// ThinkGem 修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动
	try {
		configuration.getTypeAliasRegistry().registerAliases(packageToScan,
				typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
	} catch (Exception ex) {
		LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);
		throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);
	} finally {
		ErrorContext.instance().reset();
	}
	// ThinkGem end
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
    }
  }
}

if (!isEmpty(this.typeAliases)) {
  for (Class<?> typeAlias : this.typeAliases) {
    configuration.getTypeAliasRegistry().registerAlias(typeAlias);
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Registered type alias: '" + typeAlias + "'");
    }
  }
}

if (!isEmpty(this.plugins)) {
  for (Interceptor plugin : this.plugins) {
    configuration.addInterceptor(plugin);
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Registered plugin: '" + plugin + "'");
    }
  }
}

if (hasLength(this.typeHandlersPackage)) {
  String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
      ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  for (String packageToScan : typeHandlersPackageArray) {
    configuration.getTypeHandlerRegistry().register(packageToScan);
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
    }
  }
}

if (!isEmpty(this.typeHandlers)) {
  for (TypeHandler<?> typeHandler : this.typeHandlers) {
    configuration.getTypeHandlerRegistry().register(typeHandler);
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Registered type handler: '" + typeHandler + "'");
    }
  }
}

if (xmlConfigBuilder != null) {
  try {
    xmlConfigBuilder.parse();

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
    }
  } catch (Exception ex) {
    throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
  } finally {
    ErrorContext.instance().reset();
  }
}

if (this.transactionFactory == null) {
  this.transactionFactory = new SpringManagedTransactionFactory();
}

configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

if (this.databaseIdProvider != null) {
  try {
    configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
  } catch (SQLException e) {
    throw new NestedIOException("Failed getting a databaseId", e);
  }
}

if (!isEmpty(this.mapperLocations)) {
  for (Resource mapperLocation : this.mapperLocations) {
    if (mapperLocation == null) {
      continue;
    }

    try {
      XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
          configuration, mapperLocation.toString(), configuration.getSqlFragments());
      xmlMapperBuilder.parse();
    } catch (Exception e) {
		// ThinkGem MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动
    	LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);
    	throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
    } finally {
      ErrorContext.instance().reset();
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
    }
  }
  
  // ThinkGem 启动刷新MapperXML定时器(有助于开发者调试)。
  new MapperRefresh(this.mapperLocations, configuration).run();
  
} else {
  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
  }
}

return this.sqlSessionFactoryBuilder.build(configuration);

}

/**

  • {@inheritDoc}
    */
    @Override
    public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
    }
return this.sqlSessionFactory;

}

/**

  • {@inheritDoc}
    */
    @Override
    public Class getObjectType() {
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
    }

/**

  • {@inheritDoc}
    */
    @Override
    public boolean isSingleton() {
    return true;
    }

/**

  • {@inheritDoc}
    */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
    if (failFast && event instanceof ContextRefreshedEvent) {
    // fail-fast -> check all statements are completed
    this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }
    }

重写SqlSessionFactoryBean就的修改下Spring的MyBatis配置部分: 

Xml代码   收藏代码
  1.   
  2.     <bean id="sqlSessionFactory" class="com.thinkgem.jeesite.common.mybatis.spring.SqlSessionFactoryBean">  
  3.         <property name="dataSource" ref="dataSource"/>  
  4.         <property name="typeAliasesPackage" value="com.thinkgem.jeesite"/>  
  5.         <property name="typeAliasesSuperType" value="<span style="line-height: 1.5;">com.thinkgem.jeesitespan><span style="line-height: 1.5;">.persistence.BaseEntity"/>span>  
  6.         <property name="mapperLocations" value="classpath*:/mappings/**/*.xml"/>  
  7.         <property name="configLocation" value="classpath:/mybatis-config.xml">property>  
  8.     bean>  

    
        
        
        
        
		
    

 

 最后附加上属性配置文件:mybatis-refresh.properties

Java代码   收藏代码
  1. #是否开启刷新线程  
  2. enabled=true  
  3. #延迟启动刷新程序的秒数  
  4. delaySeconds=60  
  5. #刷新扫描间隔的时长秒数  
  6. sleepSeconds=3  
  7. #扫描Mapper文件的资源路径  
  8. mappingPath=mappings  
#是否开启刷新线程
enabled=true
#延迟启动刷新程序的秒数
delaySeconds=60
#刷新扫描间隔的时长秒数
sleepSeconds=3
#扫描Mapper文件的资源路径
mappingPath=mappings
 

 

Java你会用,但这10点实战经验你一定不知道!
8大企业级实战项目提炼,总200+课时,限时助学2折特惠,立省4688元!
分享到:
JeeSite 4.0 规划(一) | Java如何正确地写出单例模式
评论
9 楼 u010199866 2018-06-07  
2018-06-07   15:42:44   [com.cjis.util.MapperRefresh]-[DEBUG]   [enabled] true
2018-06-07   15:42:44   [com.cjis.util.MapperRefresh]-[DEBUG]   [delaySeconds] 60
2018-06-07   15:42:44   [com.cjis.util.MapperRefresh]-[DEBUG]   [sleepSeconds] 3
2018-06-07   15:42:44   [com.cjis.util.MapperRefresh]-[DEBUG]   [mappingPath] com/cjis/mapping
2018-06-07   15:42:51   [com.cjis.util.MapperRefresh]-[DEBUG]   [location] null
2018-06-07   15:42:52   [com.cjis.util.MapperRefresh]-[DEBUG]   [configuration] org.apache.ibatis.session.Configuration@9367551
Exception in thread "MyBatis-Mapper-Refresh" java.lang.NoClassDefFoundError: com/google/common/collect/Sets
at com.cjis.util.MapperRefresh$1.run(MapperRefresh.java:101)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Sets
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
... 2 more


按照楼主的操作,报错呢  请问这是为啥
8 楼 juxiaojun114 2018-03-04  
jeesite 使用idea导入开发,启动tomcat后为啥看不到========= Enabled refresh mybatis mapper =========  这个提示信息  使用eclipse没有这个问题,是我的idea设置有问题吗?
7 楼 aaddsfdsfsdfs 2017-12-18  
真心给个赞,可以的,忒提高开发效率了,配合自己写的通用后台框架,所有业务逻辑直接用js写的话,一次部署,后面基本都是热部署了,满意
6 楼 lovewinner 2017-04-17  
5 楼 空城旧梦已秋凉 2016-11-29  
为何不了解一下nutz框架
4 楼 zhugeyangyang1994 2016-06-18  
围观 大神
3 楼 agen19866 2016-06-16  
MyBatis SqlSessionFactoryBean

文件第5行一定要这样写么?
2 楼 8465279130 2016-06-15  
  赞!!!
1 楼 greatpwx 2016-06-13  
发表评论

您还没有登录,请您登录后再发表评论

相关资源推荐

    网上同学们也有实现类似功能,但都是全部清空,全部刷新XML,这样硬件消耗比较严重,加载时间也比较长。我们只修改了几行SQL就没有必要全部加载,只需要加载修改的问题就行了。

    后来为了急需解决这个问题,进行修改MyBatis源码实现Mapper XML增量刷新,直接覆盖方式实现


					
  • mybatis热部署加载*Mapper.xml文件,手动刷新*Mapper.xml文件

    由于项目已经发布到线上,要是修改一个Mapper.xml文件的话,需要重启整个服务,这个是很耗时间的,而且在一段时间内导致服务不可用,严重影响用户

  • 的体验度。所以希望可以有一个机制可以,当修改某个mapper.xml的时候,只要重新加载这个mapper.xml就好了,参考网上的一些资料和demo,加上一些
    自己的总结,下面的代码是通过测试的,可以供你们参考和使用。
    import java.i


    					
  • mybatis热部署加载*Mapper.xml文件

  • Spring学习——手动实现Mapper.xml文件热部署

    原文转载自:https://blog.csdn.net/lovelong8808/article/details/78738086

  •  

    转载使用后,根据实际情况有做部分修改,感谢原文博主提供的刷新方法。

    注意,目前该方法只支持全量刷新,即刷新指定路径下的所有Mapper.xml文件,不支持仅刷新修改后的部分Mapper.xml。

     

    由于项目已经发布到线上,要是修改一个Mapper.xm…


    					
  • 超简单的mybatismapper文件增量热部署

    通常项目中如果修改mapper.xml文件 就要重启服务器才生效。网上虽然都有类似的教程,但是很多都是根本用不了.或者每次都是全量更新.本文基于http://blog.csdn.net/chunge48596/article/details/53539126?locationNum=1&fps=1的方法上更进一步优化,直接实现换包即用.

  • 原文中对SqlSessionFactoryBean.cla


    					
  • Mybatis的xml修改后自动刷新(不改源码)

    思路来自于网络大神的启发,后来发现需要改源码对系统继承不太方便。尝试了一下发现mybatis已经给我们留下了足够多的可扩展点。

  • jrebel下载及配置(tomcat热部署)--修改java类文件、xml文件或properties资源文件自动重新加载...

    Jrebel 介绍:

  • Jrebel 可快速实现热部署,节省了大量重启时间,提高了个人开发效率
    JRebel是一款JAVA虚拟机插件,它使得JAVA程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。JRebel使你能即时分别看到代码、类和资源的变化,你可以一个个地上传而不是一次性全部部署。当程序员在开发环境中对任何一个类或者资源作出修改的时候,这个变化会直接反应在部署好…


    					
  • Mybatis实现*mapper.xml热部署-分子级更新

    需求:

  • 项目在开发阶段或是修复bug阶段,会有修改mybatismapper.xml的时候,修改一般情况都要重启才能生失效,如果是分布式项目重启有时会耗时很久,都是无尽的等待。如果频繁修改,那么时间都浪费到等待重启的过程。

    目标:

    实现mybatismapper.xml文件修改后热部署,而且只热更新修改了的xml,可以提高重新解析过程的效率。

    要求:

    尽量满足开闭原则

    实现



    					
  • 针对IDEA使用JRebel热部署修改mybatis映射文件sql语句热部署失败的解决方案

        本人开发环境:IDEA(2017.3) JRebel(7.X)    使用过一段时间的IDEA后感觉非常好用,并且搭配JRebel后开发简直非常舒服。但是使用的过程中有个很烦的问题,就是修改sql映射文件时热部署好像没有反应。    这个问题我最后在JRebel官网的论坛上找到了答案,大家可以参考一下。同时,也友情提示一下,也许百度或谷歌搜半天的问题,去官网的论坛里搜一下关键字一下便找到问...

  • Springboot整合mybatis、以及xml配置实例、热部署、打包、跳转、ssl、webapp

    整合mybatis

  • 引入jar包,这个包是dao+server整合,内涵mybatis生成的xml,及mapper接口和bean对象

    引入包后,其包的依赖也会下来,所依赖的jar

    yml文件配置连接参数,数据源如果有引入jar则还可以配置数据源,mybatis配置mapper接口在哪里,需要文件路径配置/,在配置bean对象在哪里

    找到启动类:

    mappersc…


    					
  • eclipse jrebel properties,xml等配置文件不能热部署

    博主是装的jrebel8.2.4最新版本,装完后发现properties,xml等配置文件不能实现热部署,google了半天没有找到解决办法…至少到放弃的这一刻,还是没有找到办法,但是有个办法可以一定程度上解决这个问题,如下图操作:

  • 这个办法能够将文件部署到webapp下,但是不能加载到内存中



    					
  • SpringBoot整合Mybatis,使用通用mapper插件的时候,热部署报错,如何解决?

    SpringBoot整合Mybatis,通用mapper插件热部署报错。。。。。。在使用SpringBoot 整合mybatis的时候,为了减少不必要的代码开发量,我们会使用mybatis的通用mapper插件,tk.mapper,首先引入如下的依赖:&amp;lt;plugin&amp;gt;     &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;...

  • mac自动加载热部署

    idea的热部署

  • IDEA热部署修改mybatis映射文件工具 jr-ide-intellij-nightly.zip

    IDEA热部署修改mybatis映射文件工具 jr-ide-intellij-nightly.zip

  • 6.5热部署修改xml不重启

    6.5热部署修改xml不重启

  • spring boot中配置mybatis热加载.zip

    spring boot中配置mybatis xml资源文件热加载的方法以及相关文件

  • springBoot实现配置和实例的热更新,集成Apollo,方法通用

  • web动态部署(热部署

    今天跟大家探讨一下关于web动态部署,也就是热部署的问题。说这个之前,先说一个敏捷开发的原则。

  • 【最小发布、增量开发】
    我们在做项目时,设定的期限都特别长。总是想第一个版本就想把所有想到的问题都做完,以至于项目一再延期。所以我们应该改变我们的开发策略。采用敏捷开发的方式。

    这里我想强调的有2点,1.最小发布。2.增量开发。

    对于最小发布,就是要在第一版中把核心功能实现,即立


    					
  • 手把手教你如何玩转SpringBoot整合MyBatis(全注解开发和热部署及其JSP配置详解)

     

  •      当写了前一篇关于SpringBoot的文章之后,有很多朋友就提问说,关于SpringBoot整合Mybatis,还有SpringBoot热部署,并且还有说关于整合JSP配置的一些问题,然后决定写这一篇文章来帮有疑惑的朋友来解决一下问题。(SpringBoot整合Hibernate的使用,及其SpringBoot的基本知识可以参考之前的一篇章  SpringBoot基础学习  )



    		
      

    你可能感兴趣的:(Mybaties)