mybatis熱部署加載*Mapper.xml文件,手動刷新*Mapper.xml文件

由於項目已經發布到線上,要是修改一個Mapper.xml文件的話,需要重啓整個服務,這個是很耗時間的,而且在一段時間內導致服務不可用,嚴重影響用戶

的體驗度。所以希望可以有一個機制可以,當修改某個mapper.xml的時候,只要重新加載這個mapper.xml就好了,參考網上的一些資料和demo,加上一些

自己的總結,下面的代碼是通過測試的,可以供你們參考和使用。

[java] view plain copy
  1. import java.io.IOException;    
  2. import java.lang.reflect.Field;    
  3. import java.util.ArrayList;  
  4. import java.util.HashMap;    
  5. import java.util.List;  
  6. import java.util.Map;    
  7. import java.util.Set;    
  8.     
  9. import org.apache.commons.logging.Log;    
  10. import org.apache.commons.logging.LogFactory;    
  11. import org.apache.ibatis.builder.xml.XMLMapperBuilder;    
  12. import org.apache.ibatis.session.Configuration;    
  13. import org.apache.ibatis.session.SqlSessionFactory;    
  14. import org.springframework.core.io.Resource;    
  15. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;    
  16.     
  17. public class RefreshMapperCache {    
  18.     private Log log  = LogFactory.getLog(RefreshMapperCache.class);    
  19.       
  20.     private SqlSessionFactory sqlSessionFactory;    
  21.     private Resource[] mapperLocations;    
  22.     private String packageSearchPath;    
  23.     private HashMap<String, Long> fileMapping = new HashMap<String, Long>();// 記錄文件是否變化    
  24.       
  25.     //記錄發生改變的xml文件名稱  
  26.     private List<String> changeResourceNameList = new ArrayList<>();  
  27.         
  28.     public void refreshMapper() {    
  29.         try {    
  30.             Configuration configuration = this.sqlSessionFactory.getConfiguration();    
  31.                 
  32.             // step.1 掃描文件    
  33.             try {    
  34.                 this.scanMapperXml();    
  35.             } catch (IOException e) {    
  36.                 log.error("packageSearchPath掃描包路徑配置錯誤");    
  37.                 return;    
  38.             }    
  39.                 
  40. //            System.out.println("==============刷新前mapper中的內容 start===============");    
  41. //            //獲取xml中的每個語句的名稱即 id = "findUserById";  
  42. //            for (String name : configuration.getMappedStatementNames()) {    
  43. //                System.out.println(name);    
  44. //            }    
  45. //            System.out.println("==============刷新前mapper中的內容   end===============");    
  46.               
  47.             //清空被修改過後的文件名稱,確保該集合是空的  
  48.             changeResourceNameList.clear();  
  49.             // step.2 判斷是否有文件發生了變化    
  50.             if (this.isChanged()) {    
  51.                 // step.2.1 清理    
  52.                 this.removeConfig(configuration);    
  53.     
  54.                 // step.2.2 重新加載    
  55.                 for (Resource configLocation : mapperLocations) {    
  56.                     try {   
  57.                         //匹配被修改過的mapper文件,如果存在,則重新加載  
  58.                         //如果想要重新加載全部mapper,可以不匹配  
  59.                         if(changeResourceNameList.contains(configLocation.getFilename())){  
  60.                              XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configLocation.getInputStream(), configuration, configLocation.toString(), configuration.getSqlFragments());    
  61.                              xmlMapperBuilder.parse();    
  62.                              System.out.println("mapper文件[" + configLocation.getFilename() + "]緩存加載成功");    
  63.                         }  
  64.                     } catch (IOException e) {    
  65.                         System.out.println("mapper文件[" + configLocation.getFilename() + "]不存在或內容格式不對");    
  66.                         continue;    
  67.                     }    
  68.                 }  
  69.                 //清空被修改過後的文件名稱  
  70.                 changeResourceNameList.clear();  
  71.             }    
  72.                 
  73. //            System.out.println("--------------------------刷新後mapper中的內容 start--------------------------");    
  74. //            for (String name : configuration.getMappedStatementNames()) {    
  75. //                System.out.println(name);    
  76. //            }  
  77. //            System.out.println("--------------------------刷新後mapper中的內容  end--------------------------");     
  78.         } catch (Exception e) {    
  79.            System.out.println("****************刷新緩存異常: "+e.getMessage());  
  80.         }    
  81.     }    
  82.         
  83.     public void setPackageSearchPath(String packageSearchPath) {    
  84.         this.packageSearchPath = packageSearchPath;    
  85.     }    
  86.         
  87.     public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {    
  88.         this.sqlSessionFactory = sqlSessionFactory;    
  89.     }    
  90.     
  91.     /**  
  92.      * 掃描xml文件所在的路徑  
  93.      * @throws IOException   
  94.      */    
  95.     private void scanMapperXml() throws IOException {    
  96.         this.mapperLocations = new PathMatchingResourcePatternResolver().getResources(packageSearchPath);    
  97.     }    
  98.     
  99.     /**  
  100.      * 清空Configuration中幾個重要的緩存  
  101.      * @param configuration  
  102.      * @throws Exception  
  103.      */    
  104.     private void removeConfig(Configuration configuration) throws Exception {    
  105.         Class<?> classConfig = configuration.getClass();    
  106.         clearMap(classConfig, configuration, "mappedStatements");    
  107.         clearMap(classConfig, configuration, "caches");    
  108.         clearMap(classConfig, configuration, "resultMaps");    
  109.         clearMap(classConfig, configuration, "parameterMaps");    
  110.         clearMap(classConfig, configuration, "keyGenerators");    
  111.         clearMap(classConfig, configuration, "sqlFragments");    
  112.     
  113.         clearSet(classConfig, configuration, "loadedResources");    
  114.     
  115.     }    
  116.     
  117.     @SuppressWarnings("rawtypes")    
  118.     private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {    
  119.         Field field = classConfig.getDeclaredField(fieldName);    
  120.         field.setAccessible(true);    
  121.         Map mapConfig = (Map) field.get(configuration);    
  122.         mapConfig.clear();    
  123.     }    
  124.     
  125.     @SuppressWarnings("rawtypes")    
  126.     private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {    
  127.         Field field = classConfig.getDeclaredField(fieldName);    
  128.         field.setAccessible(true);    
  129.         Set setConfig = (Set) field.get(configuration);    
  130.         setConfig.clear();    
  131.     }    
  132.         
  133.     /**  
  134.      * 判斷文件是否發生了變化  
  135.      * @param resource  
  136.      * @return  
  137.      * @throws IOException  
  138.      */    
  139.     private boolean isChanged() throws IOException {    
  140.         boolean flag = false;    
  141.         System.out.println("***************************獲取文件名   開始************************************");  
  142.         for (Resource resource : mapperLocations) {    
  143.             String resourceName = resource.getFilename();    
  144.               
  145.             System.out.println("resourceName == " + resourceName+",  path = "+resource.getURL().getPath());  
  146.               
  147.             boolean addFlag = !fileMapping.containsKey(resourceName);// 此爲新增標識    
  148.                 
  149.             // 修改文件:判斷文件內容是否有變化    
  150.             Long compareFrame = fileMapping.get(resourceName);    
  151.             long lastFrame = resource.contentLength() + resource.lastModified();    
  152.             boolean modifyFlag = null != compareFrame && compareFrame.longValue() != lastFrame;// 此爲修改標識    
  153.               
  154.             if(addFlag){  
  155.                 System.out.println("            新增了:==="+ resourceName);  
  156.             }  
  157.             if(modifyFlag){  
  158.                 System.out.println("            修改了:==="+ resourceName);  
  159.             }  
  160.               
  161.             // 新增或是修改時,存儲文件    
  162.             if(addFlag || modifyFlag) {    
  163.                 fileMapping.put(resourceName, Long.valueOf(lastFrame));// 文件內容幀值    
  164.                 flag = true;    
  165.                 changeResourceNameList.add(resourceName);  
  166.             }    
  167.         }   
  168.         System.out.println("***************************獲取文件名   結束************************************");  
  169.         return flag;    
  170.     }    
  171. }    

寫一個實體類,然後在spring中配置改實體類的bean即可

[html] view plain copy
  1. <bean id="refreshMapperCache" class="com.company.project.util.RefreshMapperCache" >    
  2.     <!-- 掃描的映射mapper.xml的文件路徑   
  3.            這個地方要注意mapper的文件,多數據源情況下,只能掃描自己數據源下的mapper,否則會報異常          -->  
  4.       <property name="packageSearchPath" value="classpath*:mapper/trade/**/*.xml"></property>   
  5.       <!-- 配置自己的數據源 -->   
  6.       <property name="sqlSessionFactory" ref="sqlSessionFactoryPay"></property>    
  7. </bean>    


由於我們公司使用的是多數據源,所以在配置bean的時候,要給每個數據源配置一個bean,注意點就是在配置bean的時候

1. 如果是多數據源的情況 ,  掃描mapper.xml文件的時候,只能掃該數據源下的mapper.xml文件

2. 多數據源情況想,設置sqlSessionFactory 的時候,要設置爲對應的數據源

3. 如果是但數據源的情況,那麼就簡單了,只需要配置當前數據源及對應的mapper.xml文件即可

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