mybatis應用程序,由于是半自動化的sql, 有大量的sql是在xml文件中配置的,而在開發程序的過程中,通常需要邊寫sql變調試應用。但在默認情況下,xml文件里配置的sql語句是被放入到緩存中去了,每次更改有sql語句的xml文件,需要重新啟動應用,這樣工作效率很低,于是很希望有一個動態加載xml文件的功能,自動加載新的sql語句,并重新寫入到緩存中,在網上參考了很多資料,最終弄了一個簡單的東西出來,直接寫成了spring mvc的controller。代碼如下:??
package com.yihaomen.controller;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Scope("prototype")
@Controller
@RequestMapping("/sql")
public class SQLSessionCacheController {private Log log = LogFactory.getLog(SQLSessionCacheController.class);@Autowiredprivate SqlSessionFactory sqlSessionFactory;private Resource[] mapperLocations;private String packageSearchPath = "classpath*:**/mappers/*.xml";private HashMap<String, Long> fileMapping = new HashMap<String, Long>();// 記錄文件是否變化@RequestMapping("/refresh")@ResponseBodypublic String refreshMapper() {try {Configuration configuration = this.sqlSessionFactory.getConfiguration();// step.1 掃描文件try {this.scanMapperXml();} catch (IOException e) {log.error("packageSearchPath掃描包路徑配置錯誤");return "packageSearchPath掃描包路徑配置錯誤";}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);}return "刷新mybatis xml配置語句成功";} catch (Exception e) {e.printStackTrace();return "刷新mybatis xml配置語句失敗";}}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;}
}
注意事項:?
在我的應用中,mybatis配置文件放在這里的:classpath*:**/mappers/*.xml, 因此我定義死了,需要修改成自己的路徑.?
測試方法:?
可以通過controller提供的地址,直接在瀏覽器上輸入url訪問, 比如:?http://localhost:8080/sql/refresh?, 當然,你還可以通過js用ajax方式調用,都是可以的。