实现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 I d Id Id
  • @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>
  • 2016-06-13 10:40
  • 浏览 26246
  • 评论(9)
  •   <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>
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
MyBatis Mapper XML is a configuration file used in MyBatis, a Java-based persistence framework, to define SQL mappings between Java objects and database tables. The Mapper XML file contains SQL statements and mapping rules that are used to interact with the database. In the Mapper XML file, you define the SQL statements such as SELECT, INSERT, UPDATE, DELETE, etc., using the MyBatis XML syntax. You also define the mapping rules to map the result of the SQL queries to Java objects or vice versa. Here is an example of a simple Mapper XML file: ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.UserMapper"> <select id="getUserById" resultType="com.example.User"> SELECT * FROM users WHERE id = #{id} </select> <insert id="insertUser" parameterType="com.example.User"> INSERT INTO users (id, name, email) VALUES (#{id}, #{name}, #{email}) </insert> <update id="updateUser" parameterType="com.example.User"> UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id} </update> <delete id="deleteUser" parameterType="int"> DELETE FROM users WHERE id = #{id} </delete> </mapper> ``` In this example, the Mapper XML file defines four SQL statements: `getUserById`, `insertUser`, `updateUser`, and `deleteUser`. Each statement has an ID, parameterType (input parameter), resultType (output result mapping), and the actual SQL query. You can then use the defined SQL statements in your Java code by referencing the Mapper XML file and the statement ID using MyBatis API. Note that the actual mapping between Java objects and database tables is usually done through Java annotations or XML configuration files in addition to the Mapper XML file. The Mapper XML file primarily focuses on defining the SQL statements and their mappings. I hope this gives you a basic understanding of MyBatis Mapper XML. Let me know if you have any further questions!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值