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文件重新加载一遍,如果需要对指定文件进行加载也是可以的,需要修改下重新加载的类。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
必备 官网: https://gitee.com/topfox/topfox 文中例子源码: https://gitee.com/topfox/topfox-sample TopFox技术交流群 QQ: 874732179 2.2. topfox 介绍 在 srpingboot2.x.x 和MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 编程规范参考《阿里巴巴Java开发手册》 借鉴 mybaties plus 部分思想 特性: 无侵入:只做增强不做改变,引入它不会对现有工程产生影响 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 集成Redis缓存: 自带Redis缓存功能, 支持多主键模式, 自定义redis-key. 实现对数据库的所有操作, 自动更新到Redis, 而不需要你自己写任何代码; 当然也可以针对某个表关闭. 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 支持主键自动生成:可自由配置,充分利用Redis提高性能, 完美解决主键问题. 支持多主键查询、修改等 内置分页实现:基于 MyBatis 物理分页,开发者无需关心具体操作,写分页等同于普通查询 支持devtools/jrebel热部署 热加载 支持在不使用devtools/jrebel的情况下, 热加载 mybatismapper文件 内置全局、局部拦截插件:提供delete、update 自定义拦截功能 拥有预防Sql注入攻击功能 无缝支持spring cloud: 后续提供分布式调用的例子
请仔细阅读 README.md ## 1.1. 必备 - 文中涉及的例子源码网址: https://gitee.com/topfox/topfox-sample - TopFox技术交流群 QQ: 874732179 ## 1.2. topfox 介绍 在 srpingboot2.x.x 和MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 编程规范参考《阿里巴巴Java开发手册》 借鉴mybaties plus部分思想 特性: - **无侵入**:只做增强不做改变,引入它不会对现有工程产生影响 - **损耗小**:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 - **集成Redis缓存**: 自带Redis缓存功能, 支持多主键模式, 自定义redis-key. 实现对数据库的所有操作, 自动更新到Redis, 而不需要你自己写任何代码; 当然也可以针对某个表关闭. - **强大的 CRUD 操作**:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 - **支持 Lambda 形式调用**:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 - **支持主键自动生成**:可自由配置,充分利用Redis提高性能, 完美解决主键问题. 支持多主键查询、修改等 - **内置分页实现**:基于 MyBatis 物理分页,开发者无需关心具体操作,写分页等同于普通查询 - **支持devtools/jrebel热部署** - **热加载** 支持在不使用devtools/jrebel的情况下, 热加载 mybatismapper文件 - 内置全局、局部拦截插件:提供delete、update 自定义拦截功能 - **拥有预防Sql注入攻击功能** - **无缝支持spring cloud**: 后续提供分布式调用的例子

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值