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

来源:互联网 发布:蒙自电视台网络直播 编辑:程序博客网 时间:2024/05/17 04:36

需求: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内容如下:

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

SqlMapConfig.xml内容如下:

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

Users.xml内容如下:

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


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

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


 

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

package com.pingpeng.misp.common;import java.io.IOException;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;import java.util.Set;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.ibatis.builder.xml.XMLMapperBuilder;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.SqlSessionFactory;import org.springframework.core.io.Resource;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;public class SqlSessionCache {private Log log  = LogFactory.getLog(SqlSessionCache.class);private SqlSessionFactory sqlSessionFactory;private Resource[] mapperLocations;private String packageSearchPath;private HashMap<String, Long> fileMapping = new HashMap<String, Long>();// 记录文件是否变化public void refreshMapper() {try {Configuration configuration = this.sqlSessionFactory.getConfiguration();// step.1 扫描文件try {this.scanMapperXml();} catch (IOException e) {log.error("packageSearchPath扫描包路径配置错误");return;}System.out.println("==============刷新前mapper中的内容===============");for (String name : configuration.getMappedStatementNames()) {System.out.println(name);}// step.2 判断是否有文件发生了变化if (this.isChanged()) {// step.2.1 清理this.removeConfig(configuration);// step.2.2 重新加载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;}}}System.out.println("==============刷新后mapper中的内容===============");for (String name : configuration.getMappedStatementNames()) {System.out.println(name);}} 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();}/** * 判断文件是否发生了变化 * @param resource * @return * @throws IOException */private boolean isChanged() throws IOException {boolean flag = false;for (Resource resource : mapperLocations) {String resourceName = resource.getFilename();boolean addFlag = !fileMapping.containsKey(resourceName);// 此为新增标识// 修改文件:判断文件内容是否有变化Long compareFrame = fileMapping.get(resourceName);long lastFrame = resource.contentLength() + resource.lastModified();boolean modifyFlag = null != compareFrame && compareFrame.longValue() != lastFrame;// 此为修改标识// 新增或是修改时,存储文件if(addFlag || modifyFlag) {fileMapping.put(resourceName, Long.valueOf(lastFrame));// 文件内容帧值flag = true;}}return flag;}}


总结:

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

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

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




0 0
原创粉丝点击