mybatis實現mapper文件熱部署



 每次修改mybatis的sql腳本後,都要重啓,因爲mybatis的mapper文件默認只在啓動時加載到緩存,改動後不會自動加載,於是研究了下mybatis配置文件的加載,分享如下:

實現思路:使用定時器定時掃描mapper文件的改動,如果有改動則調用mapper文件的加載方法XMLMapperBuilder.parse()。

一.寫一個重新加載mapper文件的java類

 首先需要構建一個sqlSessionFactory對象,並指定mybatis的Configuration.xml配置文件路徑,之後定時掃描並判斷mapper文件是否有改動,如果有改動則重新加載,

代碼如下:

package zttc.itat.user.utils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * mybatis的mapper文件有改動時,進行重新加載
 * @author ycblus
 *
 */
public class SqlSessionCache {     
    private Logger log  = Logger.getLogger(SqlSessionCache.class);          
    private Resource[] mapperLocations;     
    private String packageSearchPath = "classpath*:zttc/itat/user/mapper/*.xml";     
    SqlSessionFactory sqlSessionFactory;
    Configuration configuration;
    private HashMap<String, Long> fileMapping = new HashMap<String, Long>();// 記錄文件是否變化 
    
    {
        String resource = "Configuration.xml";
        InputStream inputStream;
        try {
            inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream) ;
            configuration = this.sqlSessionFactory.getConfiguration(); //掃描文件     
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    
    public void refreshMapper() throws Exception{         
        try {                 
            try {                 
                this.scanMapperXml();             
                } catch (IOException e) {                 
                    log.error("packageSearchPath掃描包路徑配置錯誤");                 
                    return;             
                }
                Runnable runnable = new Runnable() {  
                  public void run() {  
                      // task to run goes here  
                      try {
                      // 判斷是否有文件發生了變化             
                        if (isChanged()) {                 
                            // 清理                 
                            this.removeConfig(configuration);                  
                            // 重新加載                 
                            for (Resource configLocation : mapperLocations) {                     
                                try {                         
                                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configLocation.getInputStream(),
                                            configuration, configLocation.toString(), configuration.getSqlFragments());                         
                                    xmlMapperBuilder.parse();                         
                                    log.info("mapper文件[" + configLocation.getFilename() + "]加載成功");                     
                                    } catch (IOException e) {                         
                                        log.error("mapper文件[" + configLocation.getFilename() + "]不存在或內容格式不對");                         
                                        continue;                     
                                    }                 
                                }             
                            } 
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                 } 
                  
                  /**      * 清空Configuration中幾個重要的緩存      * @param configuration      * @throws Exception      */     
                  private void removeConfig(Configuration configuration) throws Exception {         
                      Class<?> classConfig = configuration.getClass();         
                      clearMap(classConfig, configuration, "mappedStatements");         
                      clearMap(classConfig, configuration, "caches");         
                      clearMap(classConfig, configuration, "resultMaps");         
                      clearMap(classConfig, configuration, "parameterMaps");         
                      clearMap(classConfig, configuration, "keyGenerators");         
                      clearMap(classConfig, configuration, "sqlFragments");          
                      clearSet(classConfig, configuration, "loadedResources");      
                  }      
                  
                  /**      * 判斷文件是否發生了變化      * @param resource      * @return      * @throws IOException      */     
                  boolean isChanged() throws IOException {         
                      boolean flag = false;         
                      for (Resource resource : mapperLocations) {             
                          String resourceName = resource.getFilename();                          
                          boolean addFlag = !fileMapping.isEmpty() && !fileMapping.containsKey(resourceName);// 此爲新增標識                          
                          // 修改文件:判斷文件內容是否有變化             
                          Long compareFrame = fileMapping.get(resourceName);             
                          long lastFrame = resource.contentLength() + resource.lastModified();             
                          boolean modifyFlag = null != compareFrame && compareFrame.longValue() != lastFrame;// 此爲修改標識 
                          
                          fileMapping.put(resourceName, Long.valueOf(lastFrame));// 文件內容幀值
                          // 新增或是修改時,存儲文件             
                          if(addFlag || modifyFlag) {
                              flag = true;             
                          }         
                      }         
                      return flag;     
                  } 
              }; 
              ScheduledExecutorService service = Executors  
                      .newSingleThreadScheduledExecutor();  
              // 第二個參數爲首次執行的延時時間,第三個參數爲定時執行的間隔時間  
              service.scheduleAtFixedRate(runnable, 1, 10, TimeUnit.SECONDS);  
                                     
        } catch (Exception e) {             
            e.printStackTrace();         
        }     
    }          
    public void setPackageSearchPath(String packageSearchPath) {         
        this.packageSearchPath = packageSearchPath;     
    }     
    
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {         
        this.sqlSessionFactory = sqlSessionFactory;     
    }  
    
    /**      * 掃描xml文件所在的路徑      * @throws IOException       */     
    private void scanMapperXml() throws IOException {         
        this.mapperLocations = new PathMatchingResourcePatternResolver().getResources(packageSearchPath);     
    }      
    
    /**      * 清空Configuration中幾個重要的緩存      * @param configuration      * @throws Exception      */     
    private void removeConfig(Configuration configuration) throws Exception {         
        Class<?> classConfig = configuration.getClass();         
        clearMap(classConfig, configuration, "mappedStatements");         
        clearMap(classConfig, configuration, "caches");         
        clearMap(classConfig, configuration, "resultMaps");         
        clearMap(classConfig, configuration, "parameterMaps");         
        clearMap(classConfig, configuration, "keyGenerators");         
        clearMap(classConfig, configuration, "sqlFragments");          
        clearSet(classConfig, configuration, "loadedResources");      
    }      
    
    @SuppressWarnings("rawtypes")     
    private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {         
        Field field = classConfig.getDeclaredField(fieldName);         
        field.setAccessible(true);         
        Map mapConfig = (Map) field.get(configuration);         
        mapConfig.clear();     
    }  
    
    @SuppressWarnings("rawtypes")     
    private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {         
        Field field = classConfig.getDeclaredField(fieldName);         
        field.setAccessible(true);         
        Set setConfig = (Set) field.get(configuration);         
        setConfig.clear();     
    }     
    
    public static void main(String[] args) {
        HashMap<String, Long> fileMapping = new HashMap<String, Long>();
        boolean f = !fileMapping.isEmpty() && !fileMapping.containsKey("Hello.xml");
        System.out.println(f);
    }
}

 

二.Configuration.xml配置文件

這個配置文件在這裏只作爲掃描配置使用,可以看到其他配置都沒有,因爲我是在spring裏面統一配置了

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 <configuration>
     <mappers>
         <mapper resource="zttc/itat/user/mapper/TUserMapper.xml"/>
         <mapper resource="zttc/itat/user/mapper/TStoreMapper.xml"/>
     </mappers>
 </configuration>


三.寫一個Servlet,在服務器啓動時調用前面寫好的定時加載類。

代碼如下:

 

package zttc.itat.user.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.apache.log4j.Logger;

import zttc.itat.user.utils.SqlSessionCache;

/**
 * Servlet implementation class MapperReloadServlet
 * 
 * when mybatis files changed,reload them  
 */
public class MapperReloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MapperReloadServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    public void init()throws ServletException
    {
      Logger logger = Logger.getLogger(this.getClass());
      logger.info("The mapper reload timer starting... ");
      
      try {
        new SqlSessionCache().refreshMapper();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }        
    }
}

此外,需在web.xml中加入這個servlet類,添加代碼如下:

 <servlet>
    <servlet-name>MapperReloadServlet</servlet-name>
    <servlet-class>zttc.itat.user.servlet.MapperReloadServlet</servlet-class>
    <load-on-startup>7</load-on-startup>
  </servlet> 

這樣每次當mapper文件有改動時,就會重新加載。不過會把所有的mapper文件重新加載一遍,如果需要對指定文件進行加載也是可以的,需要修改下重新加載的類。


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