實現MyBatis Mapper XML文件增量動態刷新,自動加載,熱加載,熱部署

閱讀更多

    最初啓動服務後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<String> 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<File> 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<File> getRefreshFile(File dir, Long beforeTime) {  
  226.         List<File> fileList = new ArrayList<File>();  
  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<V> extends HashMap<String, V> {  
  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, Map<String, ? extends 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<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.");
     }
    
     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<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);
      }

    /**

    • 重寫 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;
       }
      

      }
      }
      }

 

 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<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {  
  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 "&lt;sqlmapper&gt;" 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 &lt;properties&gt;} 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.    * <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> 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.    * <b>NOTE:</b> This class <em>overrides</em> 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 Class<? extends 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 IdId
  • @modify ThinkGem 2016-5-24 來自 MyBatisSpring1.2.3版本
    */
    public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

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 “&lt;sqlmapper&gt;” 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 &lt;properties&gt;} 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.
  • <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> 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;
    }

/**

  • <b>NOTE:</b> This class <em>overrides</em> 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&lt;?&gt; 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&lt;?&gt; 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<? extends SqlSessionFactory> 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. <!-- MyBatis SqlSessionFactoryBean -->  
  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.jeesite</span><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 SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="com.thinkgem.jeesite.common.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.thinkgem.jeesite"/>
        <property name="typeAliasesSuperType" value="com.thinkgem.jeesite.persistence.BaseEntity"/>
        <property name="mapperLocations" value="classpath*:/mappings/**/*.xml"/>
		<property name="configLocation" value="classpath:/mybatis-config.xml"></property>
    </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元!
<div id="share_weibo">分享到:
  <a data-type="sina" href="javascript:;" title="分享到新浪微博"><img src="/images/sina.jpg"></a>
  <a data-type="qq" href="javascript:;" title="分享到騰訊微博"><img src="/images/tec.jpg"></a>
</div>
  <li>分類:<a href="https://www.iteye.com/blogs/category/architecture">企業架構</a></li>      
  <li class="last"><a href="https://www.iteye.com/wiki/blog/2304557" target="_blank" class="more">查看更多</a></li>
</ul>    
評論
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  
相關資源推薦
					<li class="news-recommends-ajax">
          
          <a href="https://download.csdn.net/download/qq_38686665/10202917" data-track-click="{&quot;con&quot;:&quot;,https://download.csdn.net/download/qq_38686665/10202917,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="mybatis熱部署mapper增量更新."><em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱部署</em><em class="related_suggestion_highlight">mapper</em><em class="related_suggestion_highlight">增量</em>更新.</a>
          <p>
            通常項目中如果修改<em class="related_suggestion_highlight">mapper</em>.<em class="related_suggestion_highlight">xml文件</em> 就要重啓服務器才生效.本資源直接換包即用.<em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">熱部署</em>
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/baochanghong/article/details/51939115" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/baochanghong/article/details/51939115,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="實現MyBatis Mapper XML文件增量動態刷新,自動加載,熱加載,熱部署"><em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">MyBatis</em> <em class="related_suggestion_highlight">Mapper</em> <em class="related_suggestion_highlight">XML文件</em><em class="related_suggestion_highlight">增量</em><em class="related_suggestion_highlight">動態刷新</em>,<em class="related_suggestion_highlight">自動加載</em>,<em class="related_suggestion_highlight">熱加載</em>,<em class="related_suggestion_highlight">熱部署</em></a>
          <p>
            最初啓動服務後<em class="related_suggestion_highlight">Mapper</em>&nbsp;<em class="related_suggestion_highlight">XML文件</em>,必須重啓服務才能生效,這樣就大大影響了我們的開發效率。

    網上同學們也有實現類似功能,但都是全部清空,全部刷新XML,這樣硬件消耗比較嚴重,加載時間也比較長。我們只修改了幾行SQL就沒有必要全部加載,只需要加載修改的問題就行了。

    後來爲了急需解決這個問題,進行修改MyBatis源碼實現Mapper XML增量刷新,直接覆蓋方式實現


					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/LOVELONG8808/article/details/78738086" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/LOVELONG8808/article/details/78738086,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="mybatis熱部署加載*Mapper.xml文件,手動刷新*Mapper.xml文件"><em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱部署</em>加載*<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em>,手動刷新*<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em></a>
          <p>
            由於項目已經發布到線上,要是修改一個<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em>的話,需要重啓整個服務,這個是很耗時間的,而且在一段時間內導致服務不可用,嚴重影響用戶

的體驗度。所以希望可以有一個機制可以,當修改某個mapper.xml的時候,只要重新加載這個mapper.xml就好了,參考網上的一些資料和demo,加上一些
自己的總結,下面的代碼是通過測試的,可以供你們參考和使用。
import java.i


					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/CS568591377/article/details/37935571" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/CS568591377/article/details/37935571,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="mybatis熱部署加載*Mapper.xml文件"><em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱部署</em>加載*<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em></a>
          <p>
            
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/sinat_35626559/article/details/80988053" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/sinat_35626559/article/details/80988053,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="Spring學習——手動實現Mapper.xml文件的熱部署">Spring學習——手動<em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em>的<em class="related_suggestion_highlight">熱部署</em></a>
          <p>
            原文轉載自:https://blog.csdn.net/lovelong8808/article/details/78738086

 

轉載使用後,根據實際情況有做部分修改,感謝原文博主提供的刷新方法。

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

 

由於項目已經發布到線上,要是修改一個Mapper.xm…


					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/qq_38686665/article/details/79046328" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/qq_38686665/article/details/79046328,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="超簡單的mybatis的mapper文件增量熱部署">超簡單的<em class="related_suggestion_highlight">mybatis</em>的<em class="related_suggestion_highlight">mapper</em>文件<em class="related_suggestion_highlight">增量</em><em class="related_suggestion_highlight">熱部署</em></a>
          <p>
            通常項目中如果修改<em class="related_suggestion_highlight">mapper</em>.<em class="related_suggestion_highlight">xml文件</em> 就要重啓服務器才生效。網上雖然都有類似的教程,但是很多都是根本用不了.或者每次都是全量更新.本文基於http://blog.csdn.net/chunge48596/article/details/53539126?locationNum=1&amp;fps=1的方法上更進一步優化,直接<em class="related_suggestion_highlight">實現</em>換包即用.

原文中對SqlSessionFactoryBean.cla


					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/u014780287/article/details/78800697" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/u014780287/article/details/78800697,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/u014780287/article/details/78800697,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="Mybatis的xml修改後自動刷新(不改源碼)"><em class="related_suggestion_highlight">Mybatis</em>的xml修改後自動刷新(不改源碼)</a>
          <p>
            思路來自於網絡大神的啓發,後來發現需要改源碼對系統繼承不太方便。嘗試了一下發現<em class="related_suggestion_highlight">mybatis</em>已經給我們留下了足夠多的可擴展點。
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/iteye_14109/article/details/82400826" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/iteye_14109/article/details/82400826,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/iteye_14109/article/details/82400826,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="jrebel下載及配置(tomcat熱部署)--修改java類文件、xml文件或properties資源文件自動重新加載...">jrebel下載及配置(tomcat<em class="related_suggestion_highlight">熱部署</em>)--修改java類文件、<em class="related_suggestion_highlight">xml文件</em>或properties資源文件自動重新加載...</a>
          <p>
            Jrebel 介紹:

Jrebel 可快速實現熱部署,節省了大量重啓時間,提高了個人開發效率
JRebel是一款JAVA虛擬機插件,它使得JAVA程序員能在不進行重部署的情況下,即時看到代碼的改變對一個應用程序帶來的影響。JRebel使你能即時分別看到代碼、類和資源的變化,你可以一個個地上傳而不是一次性全部部署。當程序員在開發環境中對任何一個類或者資源作出修改的時候,這個變化會直接反應在部署好…


					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/chao_1990/article/details/85116284" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/chao_1990/article/details/85116284,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/chao_1990/article/details/85116284,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="Mybatis實現*mapper.xml熱部署-分子級更新"><em class="related_suggestion_highlight">Mybatis</em><em class="related_suggestion_highlight">實現</em>*<em class="related_suggestion_highlight">mapper</em>.xml<em class="related_suggestion_highlight">熱部署</em>-分子級更新</a>
          <p>
            需求:

項目在開發階段或是修復bug階段,會有修改mybatismapper.xml的時候,修改一般情況都要重啓才能生失效,如果是分佈式項目重啓有時會耗時很久,都是無盡的等待。如果頻繁修改,那麼時間都浪費到等待重啓的過程。

目標:

實現mybatismapper.xml文件修改後熱部署,而且只熱更新修改了的xml,可以提高重新解析過程的效率。

要求:

儘量滿足開閉原則

實現



					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/a4475686/article/details/79378000" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/a4475686/article/details/79378000,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/a4475686/article/details/79378000,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="針對IDEA使用JRebel熱部署修改mybatis映射文件sql語句熱部署失敗的解決方案">針對IDEA使用JRebel<em class="related_suggestion_highlight">熱部署</em>修改<em class="related_suggestion_highlight">mybatis</em>映射文件sql語句<em class="related_suggestion_highlight">熱部署</em>失敗的解決方案</a>
          <p>
            &nbsp; &nbsp; 本人開發環境:IDEA(2017.3) JRebel(7.X)&nbsp; &nbsp; 使用過一段時間的IDEA後感覺非常好用,並且搭配JRebel後開發簡直非常舒服。但是使用的過程中有個很煩的問題,就是修改sql映射文件時<em class="related_suggestion_highlight">熱部署</em>好像沒有反應。&nbsp; &nbsp; 這個問題我最後在JRebel官網的論壇上找到了答案,大家可以參考一下。同時,也友情提示一下,也許百度或谷歌搜半天的問題,去官網的論壇裏搜一下關鍵字一下便找到問...
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/Java_Dmz/article/details/83381725" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/Java_Dmz/article/details/83381725,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/Java_Dmz/article/details/83381725,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="Springboot整合mybatis、以及xml配置實例、熱部署、打包、跳轉、ssl、webapp">Springboot整合<em class="related_suggestion_highlight">mybatis</em>、以及xml配置實例、<em class="related_suggestion_highlight">熱部署</em>、打包、跳轉、ssl、webapp</a>
          <p>
            整合<em class="related_suggestion_highlight">mybatis</em>:

引入jar包,這個包是dao+server整合,內涵mybatis生成的xml,及mapper接口和bean對象

引入包後,其包的依賴也會下來,所依賴的jar

yml文件配置連接參數,數據源如果有引入jar則還可以配置數據源,mybatis配置mapper接口在哪裏,需要文件路徑配置/,在配置bean對象在哪裏

找到啓動類:

mappersc…


					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/yaoayao123/article/details/86558908" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/yaoayao123/article/details/86558908,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/yaoayao123/article/details/86558908,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="eclipse jrebel properties,xml等配置文件不能熱部署">eclipse jrebel properties,xml等配置文件不能<em class="related_suggestion_highlight">熱部署</em></a>
          <p>
            博主是裝的jrebel8.2.4最新版本,裝完後發現properties,xml等配置文件不能<em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">熱部署</em>,google了半天沒有找到解決辦法…至少到放棄的這一刻,還是沒有找到辦法,但是有個辦法可以一定程度上解決這個問題,如下圖操作:

這個辦法能夠將文件部署到webapp下,但是不能加載到內存中



					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/Sopp_Li/article/details/79800169" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/Sopp_Li/article/details/79800169,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/Sopp_Li/article/details/79800169,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="SpringBoot整合Mybatis,使用通用mapper插件的時候,熱部署報錯,如何解決?">SpringBoot整合<em class="related_suggestion_highlight">Mybatis</em>,使用通用<em class="related_suggestion_highlight">mapper</em>插件的時候,<em class="related_suggestion_highlight">熱部署</em>報錯,如何解決?</a>
          <p>
            SpringBoot整合<em class="related_suggestion_highlight">Mybatis</em>,通用<em class="related_suggestion_highlight">mapper</em>插件<em class="related_suggestion_highlight">熱部署</em>報錯。。。。。。在使用SpringBoot 整合<em class="related_suggestion_highlight">mybatis</em>的時候,爲了減少不必要的代碼開發量,我們會使用<em class="related_suggestion_highlight">mybatis</em>的通用<em class="related_suggestion_highlight">mapper</em>插件,tk.<em class="related_suggestion_highlight">mapper</em>,首先引入如下的依賴:&amp;amp;lt;plugin&amp;amp;gt;				&nbsp;&nbsp;&nbsp;&nbsp;&amp;amp;lt;groupId&amp;amp;gt;org.springframework.boot&amp;amp;lt;/groupId&amp;amp;...
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/love_dl_forever/article/details/79106800" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/love_dl_forever/article/details/79106800,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/love_dl_forever/article/details/79106800,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="mac自動加載即熱部署">mac<em class="related_suggestion_highlight">自動加載</em>即<em class="related_suggestion_highlight">熱部署</em></a>
          <p>
            idea的<em class="related_suggestion_highlight">熱部署</em>
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://download.csdn.net/download/liuming690452074/11181158" data-track-click="{&quot;con&quot;:&quot;,https://download.csdn.net/download/liuming690452074/11181158,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://download.csdn.net/download/liuming690452074/11181158,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="IDEA熱部署修改mybatis映射文件工具 jr-ide-intellij-nightly.zip">IDEA<em class="related_suggestion_highlight">熱部署</em>修改<em class="related_suggestion_highlight">mybatis</em>映射文件工具 jr-ide-intellij-nightly.zip</a>
          <p>
            IDEA<em class="related_suggestion_highlight">熱部署</em>修改<em class="related_suggestion_highlight">mybatis</em>映射文件工具 jr-ide-intellij-nightly.zip
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://download.csdn.net/download/qinzhengtime/9940461" data-track-click="{&quot;con&quot;:&quot;,https://download.csdn.net/download/qinzhengtime/9940461,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://download.csdn.net/download/qinzhengtime/9940461,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="6.5熱部署修改xml不重啓">6.5<em class="related_suggestion_highlight">熱部署</em>修改xml不重啓</a>
          <p>
            6.5<em class="related_suggestion_highlight">熱部署</em>修改xml不重啓
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://download.csdn.net/download/mate_ge/10233712" data-track-click="{&quot;con&quot;:&quot;,https://download.csdn.net/download/mate_ge/10233712,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://download.csdn.net/download/mate_ge/10233712,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="spring boot中配置mybatis熱加載.zip">spring boot中配置<em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱加載</em>.zip</a>
          <p>
            spring boot中配置<em class="related_suggestion_highlight">mybatis</em> xml資源文件<em class="related_suggestion_highlight">熱加載</em>的方法以及相關文件
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/qq_32635069/article/details/83655625" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/qq_32635069/article/details/83655625,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/qq_32635069/article/details/83655625,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="springBoot實現配置和實例的熱更新,集成Apollo,方法通用">springBoot<em class="related_suggestion_highlight">實現</em>配置和實例的熱更新,集成Apollo,方法通用</a>
          <p>
            
          </p>
					</li>
			
					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/xiaoxian8023/article/details/8715837" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/xiaoxian8023/article/details/8715837,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/xiaoxian8023/article/details/8715837,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="web動態部署(熱部署)">web動態部署(<em class="related_suggestion_highlight">熱部署</em>)</a>
          <p>
            今天跟大家探討一下關於web動態部署,也就是<em class="related_suggestion_highlight">熱部署</em>的問題。說這個之前,先說一個敏捷開發的原則。

【最小發布、增量開發】
我們在做項目時,設定的期限都特別長。總是想第一個版本就想把所有想到的問題都做完,以至於項目一再延期。所以我們應該改變我們的開發策略。採用敏捷開發的方式。

這裏我想強調的有2點,1.最小發布。2.增量開發。

對於最小發布,就是要在第一版中把核心功能實現,即立


					<li class="news-recommends-ajax">
          
          <a href="https://blog.csdn.net/Cs_hnu_scw/article/details/78961232" data-track-click="{&quot;con&quot;:&quot;,https://blog.csdn.net/Cs_hnu_scw/article/details/78961232,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" data-track-view="{&quot;con&quot;:&quot;,https://blog.csdn.net/Cs_hnu_scw/article/details/78961232,-&quot;,&quot;mod&quot;:&quot;popu_712&quot;}" target="_blank" title="手把手教你如何玩轉SpringBoot整合MyBatis(全註解開發和熱部署及其JSP配置詳解)">手把手教你如何玩轉SpringBoot整合<em class="related_suggestion_highlight">MyBatis</em>(全註解開發和<em class="related_suggestion_highlight">熱部署</em>及其JSP配置詳解)</a>
          <p>
            &nbsp;

     當寫了前一篇關於SpringBoot的文章之後,有很多朋友就提問說,關於SpringBoot整合Mybatis,還有SpringBoot熱部署,並且還有說關於整合JSP配置的一些問題,然後決定寫這一篇文章來幫有疑惑的朋友來解決一下問題。(SpringBoot整合Hibernate的使用,及其SpringBoot的基本知識可以參考之前的一篇章  SpringBoot基礎學習  )



		</ul>
  </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章