Spring学习——手动实现Mapper.xml文件的热部署

原文转载自:https://blog.csdn.net/lovelong8808/article/details/78738086

 

转载使用后,根据实际情况有做部分修改,感谢原文博主提供的刷新方法。

注意,目前该方法只支持全量刷新,即刷新指定路径下的所有Mapper.xml文件,不支持仅刷新修改后的部分Mapper.xml。

 

由于项目已经发布到线上,要是修改一个Mapper.xml文件的话,需要重启整个服务,这个是很耗时间的,而且在一段时间内导致服务不可用,严重影响用户的体验度。所以希望可以有一个机制可以,当修改某个mapper.xml的时候,只要重新加载这个mapper.xml就好了,参考网上的一些资料和demo,加上一些自己的总结,下面的代码是通过测试的,可以供参考和使用。

 

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * Created with IntelliJ IDEA.
 * Description: 动态刷新Mapper.xml
 */
public class RefreshMapperCache {

    private Logger log = LoggerFactory.getLogger(RefreshMapperCache.class);

    private SqlSessionFactory sqlSessionFactory;
    private Resource[] mapperLocations;
    private String packageSearchPath;

    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中的内容 start===============");
//            //获取xml中的每个语句的名称即 id = "findUserById";
//            for (String name : configuration.getMappedStatementNames()) {
//                System.out.println(name);
//            }
//            System.out.println("==============刷新前mapper中的内容   end===============");

            // 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();
                    System.out.println("mapper文件[" + configLocation.getFilename() + "]缓存加载成功");
                } catch (IOException e) {
                    log.error("mapper文件[" + configLocation.getFilename() + "]不存在或内容格式不对");
                    continue;
                }
            }
                
//            System.out.println("--------------------------刷新后mapper中的内容 start--------------------------");
//            for (String name : configuration.getMappedStatementNames()) {
//                System.out.println(name);
//            }
//            System.out.println("--------------------------刷新后mapper中的内容  end--------------------------");
        } catch (Exception e) {
            log.error("****************刷新缓存异常: " + e.getMessage());
            e.getMessage();
        }
    }

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

}

 

在Spring的配置文件 applicationContext.xml 中配置bean

<bean id="refreshMapperCache" class="com.sharp.vocb.cache.RefreshMapperCache" >
		<!-- 扫描的映射mapper.xml的文件路径
			   这个地方要注意mapper的文件,多数据源情况下,只能扫描自己数据源下的mapper,否则会报异常          -->
		<property name="packageSearchPath" value="classpath*:mappers/**/*.xml"></property>
		<!-- 配置自己的数据源 -->
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>

注意:

1. 如果是多数据源的情况 ,  扫描mapper.xml文件的时候,只能扫该数据源下的mapper.xml文件

2. 多数据源情况下,设置sqlSessionFactory 的时候,要设置为对应的数据源

3. 如果是单数据源的情况,那么就简单了,只需要配置当前数据源及对应的mapper.xml文件即可

 

调用方式,我是采用的是通过前端触发一个网络请求来调用刷新MapperCache的方法。

@Controller
@RequestMapping("/refresh")
public class RefreshController extends BaseController {

    private Logger logger = LoggerFactory.getLogger(RefreshController.class);

    @Autowired
    private RefreshMapperCache refreshMapperCache;

    /**
     * 刷新 Mapper.xml
     * @param request
     * @return
     */
    @RequestMapping("/refreshMapperCache")
    @ResponseBody
    public String refreshMapperCache(HttpServletRequest request){
        JSONObject object = new JSONObject();
        try {
            refreshMapperCache.refreshMapper();
            SysEntUser sysEntUser = getCurrentUser(request);
            logger.info("== 用户 "+ sysEntUser.getName() + " 执行了刷新MapperCache操作。");
            object.put(SystemKeyWord.RETURN_CODE, SystemKeyWord.SUCCESS_CODE);
            object.put(SystemKeyWord.RETURN_DESC, "操作成功!");
        } catch (Exception e){
            logger.error("==刷新MapperCache发生错误!  "+e.getMessage());
            e.printStackTrace();
            object.put(SystemKeyWord.RETURN_CODE, SystemKeyWord.ERROR_CODE);
            object.put(SystemKeyWord.RETURN_DESC, "操作失败!");
            object.put(SystemKeyWord.ERROR_MESSAGE, e.getMessage());
        }
        return object.toJSONString();
    }
}

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值