Mybatis手动热加载

    原代码来自网络, 重组了代码并优化了isChanged的判断 (删除/增加/更新xml均能侦测),  此热加载工具可以脱离spring容器运行, 有利于加快junit调试.

    

package com.freestyle.common.db.mybatis;

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.Map.Entry;
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.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * mybatis的mapper文件有改动时,进行手动重新加载
 * @author dgmislrh
 */
public class MybatisReloader {
	private Logger log = Logger.getLogger(MybatisReloader.class);
	private Resource[] mLastmapperLocations = null;
	private Map<Resource, Long> mvLastFingerPrint = null;
	private String packageSearchPath=null;
	SqlSessionFactory sqlSessionFactory;
	Configuration configuration;

	public MybatisReloader(String pvsPackageSearchPath, SqlSessionFactory pvSqlSessionFactory) {
		this.packageSearchPath = pvsPackageSearchPath;
		this.configuration = pvSqlSessionFactory.getConfiguration();
		this.sqlSessionFactory = pvSqlSessionFactory;
		try {
			mLastmapperLocations = this.scanMapperXml();
			mvLastFingerPrint = getFingerPrint(mLastmapperLocations);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	// private HashMap<String, Long> fileMapping = new HashMap<String,
	// Long>();// 记录文件是否变化

	private static Map<Resource, Long> getFingerPrint(Resource[] pvResources) {
		Map<Resource, Long> lvRet = new HashMap<Resource, Long>();
		for (Resource item : pvResources) {
			Long lvValue = 0L;
			try {
				lvValue = item.contentLength() + item.lastModified();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			lvRet.put(item, lvValue);
		}
		return lvRet;
	}

	public String refreshMapper(boolean pvbRunOnce) throws Exception {
		StringBuilder sb = new StringBuilder();
		try {
			Runnable runnable = new Runnable() {
				public void run() {
					// task to run goes here
					try {
						// 判断是否有文件发生了变化
						List<Resource> lvToDelete = new ArrayList<Resource>();
						List<Resource> lvToAdd= new ArrayList<Resource>();
						if (isChanged(lvToDelete,lvToAdd)) {
							// 清理
							removeConfig(configuration);
							// 重新加载
							for (Resource configLocation : mLastmapperLocations) {
								try {
									XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
											configLocation.getInputStream(), configuration, configLocation.toString(),
											configuration.getSqlFragments());
									xmlMapperBuilder.parse();
									// log.info("mapper文件[" +
									// configLocation.getFilename() + "]加载成功");
									sb.append("INFO: mapper文件[" + configLocation.getFilename() + "]加载成功\n");
								} catch (IOException e) {
									// log.error("mapper文件[" +
									// configLocation.getFilename() +
									// "]不存在或内容格式不对");
									sb.append("ERROR: mapper文件[" + configLocation.getFilename() + "]不存在或内容格式不对\n");
									continue;
								}
							}
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				/**
				 * * 判断文件是否发生了变化 * @param resource * @return * @throws
				 * IOException
				 */
				boolean isChanged(List<Resource> pvToDelete, List<Resource> pvToAdd) 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; } }
					 */
					// 获得当前的状态
					Resource[] lvLastmapperLocations = scanMapperXml();
					Map<Resource, Long> lvLastFingerPrint = getFingerPrint(lvLastmapperLocations);

					// 判断需要删除和更新的
					for (Entry<Resource, Long> item : mvLastFingerPrint.entrySet()) {
						if (!lvLastFingerPrint.containsKey(item.getKey())) {
							pvToDelete.add(item.getKey());
						} else {
							Long lvLast = lvLastFingerPrint.get(item.getKey());
							if (!lvLast.equals(item.getValue())) { // 有变动
								pvToDelete.add(item.getKey());
								pvToAdd.add(item.getKey());
							}
						}
					}
					// 判断增加的
					for (Entry<Resource, Long> item : lvLastFingerPrint.entrySet()) {
						if (!lvLastFingerPrint.containsKey(item.getKey())) {
							pvToAdd.add(item.getKey());
						}
					}
					mLastmapperLocations=lvLastmapperLocations;
					mvLastFingerPrint=lvLastFingerPrint;
					return pvToAdd.size() > 0 || pvToDelete.size() > 0;
				}
			};
			if (!pvbRunOnce) {
				ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
				// 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间
				service.scheduleAtFixedRate(runnable, 1, 10, TimeUnit.SECONDS);
				return "";
			} else {
				runnable.run();
				return sb.toString();
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}

	public void setPackageSearchPath(String packageSearchPath) {
		this.packageSearchPath = packageSearchPath;
	}

	/** * 扫描xml文件所在的路径 * @throws IOException */
	private Resource[] scanMapperXml() throws IOException {
		return new PathMatchingResourcePatternResolver().getResources(packageSearchPath);
	}

	/** * 清空Configuration中几个重要的缓存 * @param configuration * @throws Exception */
	protected static 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 static 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 static 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();
	}
}
 
 

写一个junit测试:

package test;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.logicalcobwebs.proxool.ProxoolFacade;

import com.freestyle.common.db.mybatis.MybatisReloader;
import com.freestyle.common.db.mybatis.MybatisUtils;
import com.freestyle.common.db.proxool.ProxoolUtils;
import com.freestyle.proxoolmybatisstudy.dao.TaUserMapper;
import com.freestyle.proxoolmybatisstudy.entities.TaUser;

public class Test1 {
	@Before
	public void setUp() throws Exception {
		//加载Proxool数据库连接池
		ProxoolUtils.loadProxool();
		
	}

	@After
	public void tearDown() throws Exception {
		ProxoolFacade.shutdown(0);
	}	
	@Test
	public void testReload() throws Exception{
		MybatisReloader lvReloader=new MybatisReloader("classpath:com/freestyle/proxoolmybatisstudy/dao/*.xml",MybatisUtils.getSessionFactory());
		test();
		System.out.print(lvReloader.refreshMapper(true));
		test();
	}

如果mapper没更改,则lvReloader.refreshMapper(true)返回空字符串"",如果有更改,则会返回重新加载的日志信息.

我们在lvReloader.refreshMapper(true)处打个断点, 运行到这里的时候 , 更改TaUserMapper.xml, 再接着跑下去,日志输出 :

11:15:46,050 DEBUG PathMatchingResourcePatternResolver:693 - Searching directory [C:\myproject\workspace\proxoolmybatis\target\classes\com\freestyle\proxoolmybatisstudy\dao] for files matching pattern [C:/myproject/workspace/proxoolmybatis/target/classes/com/freestyle/proxoolmybatisstudy/dao/*.xml]
11:15:46,053 DEBUG PathMatchingResourcePatternResolver:424 - Resolved location pattern [classpath:com/freestyle/proxoolmybatisstudy/dao/*.xml] to resources [file [C:\myproject\workspace\proxoolmybatis\target\classes\com\freestyle\proxoolmybatisstudy\dao\TaUserMapper.xml], file [C:\myproject\workspace\proxoolmybatis\target\classes\com\freestyle\proxoolmybatisstudy\dao\TdNotifyMapper.xml]]
INFO: mapper文件[TaUserMapper.xml]加载成功
INFO: mapper文件[TdNotifyMapper.xml]加载成功



猜你喜欢

转载自blog.csdn.net/rocklee/article/details/80507298