application.yml中
mybatis:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: mybatis/**/*Mapper.xml
typeAliasesPackage:**.domain
mapper:
reload: true
本例子是在springboot中,如果不是springboot的项目可以根据自己情况修改。
具体代码如下:
@Component
public class MapperHotDeployPlugin {
protected final Log log = LogFactory.getLog(getClass());
@Value("${mybatis.mapper-locations}")
private String packageSerchPath;
@Autowired
private SqlSessionFactory sqlSessionFactory;
private List<Resource> mapperLocations = new ArrayList<>();
private Configuration configuration;
private HashMap<String, Long> fileMapping = new HashMap<String, Long>();// 记录文件是否变化
@Value("${mybatis.mapper.reload}")
private Boolean reloadFlag;
private static Boolean refresh;
@PostConstruct
public void init() {
try {
if(!prepareEnv()) return;
Runnable runnable = new Runnable() {
public void run() {
changeCompare();
}
};
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
// 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间
service.scheduleAtFixedRate(runnable, 1, 10, TimeUnit.SECONDS);
log.info("============mapper热部署已生效=============");
} catch (Exception e) {
log.error("packageSearchPath扫描包路径配置错误");
return;
}
}
/**
* 功能描述: 准备赋值环境变量
* @param:
* @return:
* @auther: zenglingsheng@baiyyy.com.cn
* @date: 2020/10/21 8:43
*/
public boolean prepareEnv() throws Exception{
if(!reloadFlag){
return reloadFlag;
}
this.configuration = sqlSessionFactory.getConfiguration();
String [] packagePaths=packageSerchPath.split(",");
for(String item:packagePaths){
Resource[] mapperLocation = new PathMatchingResourcePatternResolver().getResources(item);
for (Resource resource : mapperLocation) {
String resourceName = resource.getFilename();
long lastFrame = resource.contentLength() + resource.lastModified();
fileMapping.put(resourceName, Long.valueOf(lastFrame));// 文件内容帧值
}
this.mapperLocations.addAll(Arrays.asList(mapperLocation));
}
return reloadFlag;
}
/**
* 功能描述:判断xml文件是否修改方法
* @param:
* @return:
* @auther: zenglingsheng@baiyyy.com.cn
* @date: 2020/10/21 8:35
*/
public void changeCompare(){
// task to run goes here
try {
refresh=true;
// 判断是否有文件发生了变化
if (isChanged()) {
log.info("-------mapper文件已全部更新-------");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 判断文件是否发生了变化 * @param resource * @return * @throws IOException
*/
boolean isChanged() throws IOException {
List<Resource> changedResource=new ArrayList<>();
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 != lastFrame;// 此为修改标识
/* System.out.println("resourceName 名字 "+resourceName+" 新大小"+lastFrame+",老大小 "+compareFrame);*/
fileMapping.put(resourceName, Long.valueOf(lastFrame));// 文件内容帧值
// 新增或是修改时,存储文件
if (addFlag || modifyFlag) {
flag = true;
changedResource.add(resource);
log.info("-------" + resourceName + "文件 已修改-------");
}
}
if(flag){
refresh(changedResource);
}
return flag;
}
/**
* 功能描述: 只更新修改的xml文件在configuration中的数据
* @param:
* @return:
* @auther: zenglingsheng@baiyyy.com.cn
* @date: 2020/12/29 9:17
*/
private void refresh(List<Resource> changedResource) {
for (int i = 0; i < changedResource.size(); i++) {
String resource = changedResource.get(i).toString();
try {
InputStream inputStream = changedResource.get(i).getInputStream();
// 清理原有资源,更新为自己的StrictMap方便,增量重新加载
String[] mapFieldNames = new String[]{
"mappedStatements", "caches",
"resultMaps", "parameterMaps",
"keyGenerators", "sqlFragments"
};
for (String fieldName : mapFieldNames){
Field field = configuration.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Map map = ((Map)field.get(configuration));
if (!(map instanceof MapperHotDeployPlugin.StrictMap)){
Map newMap = new MapperHotDeployPlugin.StrictMap(StringUtils.capitalize(fieldName) + "collection");
for (Object key : map.keySet()){
try {
newMap.put(key, map.get(key));
}catch(IllegalArgumentException ex){
newMap.put(key, ex.getMessage());
}
}
field.set(configuration, newMap);
}
}
// 清理已加载的资源标识,方便让它重新加载。
Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");
loadedResourcesField.setAccessible(true);
Set loadedResourcesSet = ((Set)loadedResourcesField.get(configuration));
loadedResourcesSet.remove(resource);
//重新编译加载资源文件。
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, configuration,
resource, configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
log.error("Failed to parse mapping resource: '" + resource + "'", e);
} finally {
ErrorContext.instance().reset();
}
}
}
/**
* 重写 org.apache.ibatis.session.Configuration.StrictMap 类
* 来自 MyBatis3.4.0版本,修改 put 方法,允许反复 put更新。
*/
public static class StrictMap<V> extends HashMap<String, V> {
protected final Log log = LogFactory.getLog(getClass());
private static final long serialVersionUID = -4950446264854982944L;
private String name;
public StrictMap(String name, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this.name = name;
}
public StrictMap(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}
public StrictMap(String name) {
super();
this.name = name;
}
public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}
@SuppressWarnings("unchecked")
public V put(String key, V value) {
// 如果现在状态为刷新,则刷新(先删除后添加)
if (MapperHotDeployPlugin.refresh) {
remove(key);
log.debug("refresh key:" + key.substring(key.lastIndexOf(".") + 1));
}
// end
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key);
}
if (key.contains(".")) {
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
super.put(shortKey, value);
} else {
super.put(shortKey, (V) new MapperHotDeployPlugin.StrictMap.Ambiguity(shortKey));
}
}
return super.put(key, value);
}
public V get(Object key) {
V value = super.get(key);
if (value == null) {
throw new IllegalArgumentException(name + " does not contain value for " + key);
}
if (value instanceof MapperHotDeployPlugin.StrictMap.Ambiguity) {
throw new IllegalArgumentException(((MapperHotDeployPlugin.StrictMap.Ambiguity) value).getSubject() + " is ambiguous in " + name
+ " (try using the full name including the namespace, or rename one of the entries)");
}
return value;
}
private String getShortName(String key) {
final String[] keyparts = key.split("\\.");
return keyparts[keyparts.length - 1];
}
protected static class Ambiguity {
private String subject;
public Ambiguity(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
}
}
}