分享

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

 instl 2015-01-24

需求:mybatis的sql文件暴露在外面,但是当我们修改了对应的mapper.xml文件后,怎样才能避免重启项目实现热加载这类配置文件呢?
环境:springMVC(其中spring3.1.1、mybatis3.2.1)


1.首先看一下mybatis映射层目录结构图如下(Users.java是实体类、SqlMapConfig.xml是管理加载所有的mapper.xml文件、Users.xml是mapper.xml文件):


Users.java内容如下:

  1. package com.pingpeng.misp.models;  
  2.   
  3. public class Users implements java.io.Serializable {  
  4.   
  5.     private static final long serialVersionUID = -6057344509602820411L;  
  6.     private Integer id;  
  7.     private Integer enable;  
  8.     private String password;  
  9.     private String account;  
  10.       
  11.     // 四个属性的get、set方法请自行添加,此处不写了  
  12.       
  13. }  

SqlMapConfig.xml内容如下:

  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <!DOCTYPE configuration   
  3. PUBLIC "-//ibatis.//DTD Config 3.0//EN"   
  4. "http://ibatis./dtd/ibatis-3-config.dtd">  
  5. <configuration>  
  6.     <typeAliases>  
  7.         <!-- Entities 参数实体 -->  
  8.         <!-- com.pingpeng.misp.models 包中的 所有Bean, 取个别名 -->  
  9.         <!-- 比如Resources类就用resource来表示 -->  
  10.         <typeAlias type="com.pingpeng.misp.models.Users" alias="users" />  
  11.     </typeAliases>  
  12.     <mappers>  
  13.         <!-- 对应Bean类的xml配置文件的路径信息 -->  
  14.         <mapper resource="com/pingpeng/misp/models/mybatis/mapper/Users.xml" />  
  15.     </mappers>  
  16. </configuration>  

Users.xml内容如下:

  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <!DOCTYPE mapper   
  3. PUBLIC "-//ibatis.//DTD Mapper 3.0//EN"   
  4. "http://ibatis./dtd/ibatis-3-mapper.dtd">  
  5. <mapper namespace="com.pingpeng.misp.models.mybatis.mapper.Users">  
  6.     <!-- 定义一条查询语句,在bean的implementation中会引用此语句的id -->  
  7.     <select id="findByName" parameterType="String" resultType="users">  
  8.         select * from users where account = #{account}  
  9.     </select>  
  10.     <select id="findAll" resultType="com.pingpeng.misp.models.Users">  
  11.         select * from users   
  12.     </select>  
  13. </mapper>  


2.再看看application.xml中关于mabatis的配置(什么数据源之类的就不贴出来了):

  1. <!-- MyBatis sqlSessionFactory 配置 mybatis-->   
  2. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">   
  3.       <property name="configLocation" value="classpath:/com/pingpeng/misp/models/mybatis/SqlMapConfig.xml" />   
  4.       <property name="dataSource" ref="dataSource" />   
  5. </bean>   
  6. <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">   
  7.       <constructor-arg index="0" ref="sqlSessionFactory" />   
  8. </bean>  
  9. <bean id="sqlSessionCache" class="com.pingpeng.misp.common.SqlSessionCache" init-method="refreshMapper">  
  10.       <!-- 扫描的映射mapper.xml的文件路径-->          
  11.       <property name="packageSearchPath" value="classpath*:com/pingpeng/misp/models/mybatis/mapper/**/*.xml"></property>  
  12.       <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>  
  13. </bean>  


 

3.最后看看com.pingpeng.misp.common.SqlSessionCache这个缓存类refreshMapper方法是如何实现刷新的

  1. package com.pingpeng.misp.common;  
  2.   
  3. import java.io.IOException;  
  4. import java.lang.reflect.Field;  
  5. import java.util.HashMap;  
  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 SqlSessionCache {  
  18.     private Log log  = LogFactory.getLog(SqlSessionCache.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.     public void refreshMapper() {  
  26.         try {  
  27.             Configuration configuration = this.sqlSessionFactory.getConfiguration();  
  28.               
  29.             // step.1 扫描文件  
  30.             try {  
  31.                 this.scanMapperXml();  
  32.             } catch (IOException e) {  
  33.                 log.error("packageSearchPath扫描包路径配置错误");  
  34.                 return;  
  35.             }  
  36.               
  37.             System.out.println("==============刷新前mapper中的内容===============");  
  38.             for (String name : configuration.getMappedStatementNames()) {  
  39.                 System.out.println(name);  
  40.             }  
  41.               
  42.             // step.2 判断是否有文件发生了变化  
  43.             if (this.isChanged()) {  
  44.                 // step.2.1 清理  
  45.                 this.removeConfig(configuration);  
  46.   
  47.                 // step.2.2 重新加载  
  48.                 for (Resource configLocation : mapperLocations) {  
  49.                     try {  
  50.                         XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configLocation.getInputStream(), configuration, configLocation.toString(), configuration.getSqlFragments());  
  51.                         xmlMapperBuilder.parse();  
  52.                         log.info("mapper文件[" + configLocation.getFilename() + "]缓存加载成功");  
  53.                     } catch (IOException e) {  
  54.                         log.error("mapper文件[" + configLocation.getFilename() + "]不存在或内容格式不对");  
  55.                         continue;  
  56.                     }  
  57.                 }  
  58.             }  
  59.               
  60.             System.out.println("==============刷新后mapper中的内容===============");  
  61.             for (String name : configuration.getMappedStatementNames()) {  
  62.                 System.out.println(name);  
  63.             }  
  64.         } catch (Exception e) {  
  65.             e.printStackTrace();  
  66.         }  
  67.     }  
  68.       
  69.     public void setPackageSearchPath(String packageSearchPath) {  
  70.         this.packageSearchPath = packageSearchPath;  
  71.     }  
  72.       
  73.     public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {  
  74.         this.sqlSessionFactory = sqlSessionFactory;  
  75.     }  
  76.   
  77.     /** 
  78.      * 扫描xml文件所在的路径 
  79.      * @throws IOException  
  80.      */  
  81.     private void scanMapperXml() throws IOException {  
  82.         this.mapperLocations = new PathMatchingResourcePatternResolver().getResources(packageSearchPath);  
  83.     }  
  84.   
  85.     /** 
  86.      * 清空Configuration中几个重要的缓存 
  87.      * @param configuration 
  88.      * @throws Exception 
  89.      */  
  90.     private void removeConfig(Configuration configuration) throws Exception {  
  91.         Class<?> classConfig = configuration.getClass();  
  92.         clearMap(classConfig, configuration, "mappedStatements");  
  93.         clearMap(classConfig, configuration, "caches");  
  94.         clearMap(classConfig, configuration, "resultMaps");  
  95.         clearMap(classConfig, configuration, "parameterMaps");  
  96.         clearMap(classConfig, configuration, "keyGenerators");  
  97.         clearMap(classConfig, configuration, "sqlFragments");  
  98.   
  99.         clearSet(classConfig, configuration, "loadedResources");  
  100.   
  101.     }  
  102.   
  103.     @SuppressWarnings("rawtypes")  
  104.     private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {  
  105.         Field field = classConfig.getDeclaredField(fieldName);  
  106.         field.setAccessible(true);  
  107.         Map mapConfig = (Map) field.get(configuration);  
  108.         mapConfig.clear();  
  109.     }  
  110.   
  111.     @SuppressWarnings("rawtypes")  
  112.     private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {  
  113.         Field field = classConfig.getDeclaredField(fieldName);  
  114.         field.setAccessible(true);  
  115.         Set setConfig = (Set) field.get(configuration);  
  116.         setConfig.clear();  
  117.     }  
  118.       
  119.     /** 
  120.      * 判断文件是否发生了变化 
  121.      * @param resource 
  122.      * @return 
  123.      * @throws IOException 
  124.      */  
  125.     private boolean isChanged() throws IOException {  
  126.         boolean flag = false;  
  127.         for (Resource resource : mapperLocations) {  
  128.             String resourceName = resource.getFilename();  
  129.               
  130.             boolean addFlag = !fileMapping.containsKey(resourceName);// 此为新增标识  
  131.               
  132.             // 修改文件:判断文件内容是否有变化  
  133.             Long compareFrame = fileMapping.get(resourceName);  
  134.             long lastFrame = resource.contentLength() + resource.lastModified();  
  135.             boolean modifyFlag = null != compareFrame && compareFrame.longValue() != lastFrame;// 此为修改标识  
  136.               
  137.             // 新增或是修改时,存储文件  
  138.             if(addFlag || modifyFlag) {  
  139.                 fileMapping.put(resourceName, Long.valueOf(lastFrame));// 文件内容帧值  
  140.                 flag = true;  
  141.             }  
  142.         }  
  143.         return flag;  
  144.     }  
  145. }  


总结:

1.可以写个定时器每隔一段时间执行SqlSessionCache中refreshMapper()方法,这样就实现热部署功能

2.注意<property name="packageSearchPath" value="classpath*:com/pingpeng/misp/models/mybatis/mapper/**/*.xml"></property>配置的路径一定要是所有的mapper映射文件,而不是总管理文件

3.当前实现如果只改动了一个文件,那么也只重新加载所有文件,如何实现只加载更新的文件呢,这个有点复杂,请自行研究




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多