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();
    }
    }
}

这样每次当mapper文件有改动时,就会重新加载。不过会把所有的mapper文件重新加载一遍,如果需要对指定文件进行加载也是可以的,需要修改下重新加载的类。

另外,服务器应该配置为reloadable="true",否则改动了东西也不会自动部署到服务上去的。

四.改动mapper文件后的效果图

时间: 2024-10-08 11:50:09

mybatis实现mapper配置文件热部署的相关文章

MyBatis关于Mapper配置文件知识集合

(1)MyBatis多参数传递之默认命名方式 对于映射器中的方法,MyBatis默认从左到右给方法的参数命名为param1.param2-,依次类推.我们可以无需借助注解,直接在SQL语句中使用这些默认名称. 首先去掉@Param注解的TeacherMapper.java如下所示(完整源码下载地址:http://down.51cto.com/data/539217): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.abc.map

MyBatis的Mapper配置文件的parameterType用法

在MyBatis的select.insert.update.delete这些元素中都提到了parameterType这个属性.MyBatis现在可以使用的parameterType有基本数据类型和JAVA复杂数据类型 基本数据类型:包含int,String,Date等.基本数据类型作为传参,只能传入一个.通过#{参数名} 即可获取传入的值 复杂数据类型:包含JAVA实体类.Map.通过#{属性名}或 #{map的KeyName}即可获取传入的值 基本数据类型参数示例: 根据班级ID查询教师列表

mybatis 的mapper配置文件sql语句中, 有时用到 大于, 小于等等

一, 用<![CDATA[   ]]>标识,例如: <if test="create_timeStart != null and create_timeStart != ''"> <![CDATA[ AND create_time >= ${create_timeStart}]]> </if> 二,转义 < >  也可以>=

关于Mybatis中Mapper是使用XML还是注解的一些思考

XML 据说可以灵活的进行注解,但是修改以后还是要重新发布程序.当然,你可以说,在Tomcat中改了,然后热加载了,不就可以了.可是一般情况下都是几台,十几台服务器.都是用发布系统,持续集成的方式部署.这点灵活性也就没什么意义了.当然,一定要说XML支持好,这点我不否认.然而在注解中支持了大部分功能,如果实在复杂一点的SQL可以使用<script>方式或者使用Provider也行. 那再说,ResultMap支持的不好,但从3.某个版本,支持使用id,这样也可以在一定程度上进行复用了. 如果再

Tomcat热部署的三种方式

热部署是指在你修改项目BUG的时候对JSP或JAVA类进行了修改在不重启WEB服务器前提下能让修改生效.但是对配置文件的修改除外! 1.直接把项目web文件夹放在webapps里. 2.在tomcat\conf\server.xml中的<host></host>内部添加<context/>标签: <Context debug="0" docBase="D:\demo1\web" path="/demo1"

Springboot 热部署中存在冲突的问题

SpringBoot热部署有2中: 1.使用 Spring Loaded 2.使用 spring-boot-devtools 具体怎么用.自己百度! 在使用第一种时候,整合SpringBoot和通用Mapper时候,-----------------> Example 时候会出现找不到实体对应的表! 这就是冲突在的存在,具体怎么解决,我没找到方法!  当然我还是想用热部署的,我就使用了第2种,但是依然也遇到了问题 在我的项目中我加入了redis 注解什么意思自己百度:出现的异常java.lang

IDEA第二章----配置git、tomcat(热部署)、database,让你的项目跑起来

第一节:下载git客户端,整合idea 由于博主公司用的git版本管理,所以本系列都是基于git版本工具的,当然SVN与git配置类似.git同样支持安装版和解压版,支持各种操作系统,我这里下载的是Windows的解压版. 选择刚才解压后的文件夹,选择cmd文件夹下的git.exe,然后点击Test查看是否连接成功. 注:如果没有配置git客户端,用git地址导入项目会提示你找不到git.exe. 第二节:配置tomcat(热部署稍后会讲到) tomcat下载安装就不在累赘,博主用的是tomca

webpack-dev-server轻量级js高速打包、热部署服务器

webpack是一个打包web项目的工具 ,可以实现css,js,less,cass,html的混淆加密,minify,结合webpack-dev-server热部署,非常方便前端页面和Nodejs的开发. webpack 安装方法 npm install webpack --save-dev npm install webpack-dev-server --save-dev 执行webpack需要在项目目录有一个配置文件 webpack.config.js . var webpack = re

Java服务器热部署的实现原理

转自:http://blog.csdn.net/chenjie19891104/article/details/42807959 在web应用开发或者游戏服务器开发的过程中,我们时时刻刻都在使用热部署.热部署的目的很简单,就是为了节省应用开发和发布的时间.比如,我们在使用Tomcat或者Jboss等应用服务器开发应用时,我们经常会开启热部署功能.热部署,简单点来说,就是我们将打包好的应用直接替换掉原有的应用,不用关闭或者重启服务器,一切就是这么简单.那么,热部署到底是如何实现的呢?在本文中,我将