欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis中mapper.xml實(shí)現(xiàn)熱加載介紹

 更新時(shí)間:2022年01月21日 14:24:12   作者:程序猿小白菜  
大家好,本篇文章主要講的是Mybatis中mapper.xml實(shí)現(xiàn)熱加載介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下

背景

有些需求可能更新sql的頻率較高,但又不想頻繁發(fā)布java應(yīng)用程序,所以mybatis-mapper.xml熱加載的需求順勢(shì)而出。

目的

只需調(diào)起加載mapper.xml的程序,無(wú)需重啟整個(gè)java應(yīng)用,低耦合。

實(shí)現(xiàn)方式

mapper.xml可以指定路徑。如springboot工程resources目錄下;亦可獨(dú)立維護(hù)在某個(gè)git倉(cāng)庫(kù),然后由程序加載到運(yùn)行機(jī)器上去。
具體加載git倉(cāng)庫(kù)到運(yùn)行機(jī)器代碼如下:

package com.jason.git;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import java.io.File;
import java.io.IOException;

/**
 * @author jason
 * @create 2022/1/19 11:39 上午
 **/
@Repository
public class GitConfigRepository {

	private static final long MIN_CHECKOUT_INTERVAL = 1000L * 10;
	private static final long MAX_LOCAL_LIFE_CYCLE = 2 * 86400 * 1000L;

	private Log log = LogFactory.getLog(GitConfigRepository.class);

	@Value("${git.repository:}")
	private String gitRepositoryURL;
	@Value("${git.branch:master}")
	private String gitBranch;
	@Value("${git.username:}")
	private String gitUsername;
	@Value("${git.password:}")
	private String gitPassword;
	@Value("${bi.meta.git.localRepository:}")
	private String localRepository;

	private long lastCheckoutTimestamp;
	private long localRepositoryTimestamp;
	private File gitDir;
	private Git git;

	public File getRepositoryDir() throws IOException, GitAPIException {
		long now = System.currentTimeMillis();
		if (now - lastCheckoutTimestamp > MIN_CHECKOUT_INTERVAL) {
			this.lastCheckoutTimestamp = now;

			if (StringUtils.isNotEmpty(localRepository)) {
				gitDir = new File(localRepository);
			} else {
				boolean isNewDir = false;
				if (gitDir != null && !gitDir.exists()) {
					gitDir = null;
				}

				if (gitDir != null) {
					if (now - localRepositoryTimestamp > MAX_LOCAL_LIFE_CYCLE) {
						localRepositoryTimestamp = 0;
						try {
							gitDir.delete();
						} catch (Exception e) {
							// do nothing
						}
						gitDir = null;
					}

					File keyFile = new File(gitDir, "global/config/config.yml");
					if (!(keyFile.exists() && keyFile.length() > 0)) {
						try {
							gitDir.delete();
						} catch (Exception e) {
							// do nothing
						}
						gitDir = null;
					}
				}

				if (gitDir == null) {
					gitDir = File.createTempFile("egret-meta", ".git");
					if (!gitDir.delete()) {
						throw new IOException("無(wú)法刪除臨時(shí)文件: " + gitDir.getAbsolutePath());
					}
					if (!gitDir.mkdir()) {
						throw new IOException("創(chuàng)建歷史Git本地目錄失敗: " + gitDir.getAbsolutePath());
					}
					gitDir.deleteOnExit();
					isNewDir = true;
					localRepositoryTimestamp = now;
				}

				if (StringUtils.isNotEmpty(gitRepositoryURL)) {
					//設(shè)置遠(yuǎn)程服務(wù)器上的用戶名和密碼
					UsernamePasswordCredentialsProvider usernamePasswordCredentialsProvider = new
							UsernamePasswordCredentialsProvider(gitUsername, gitPassword);

					if (isNewDir) {
						//克隆代碼庫(kù)命令
						CloneCommand cloneCommand = Git.cloneRepository();

						git = cloneCommand.setURI(gitRepositoryURL)
								.setBranch(gitBranch)
								.setDirectory(gitDir)
								.setCredentialsProvider(usernamePasswordCredentialsProvider)
								.call();
					}

					log.info("Checkout meta configs from. [" + gitRepositoryURL + "|" + gitBranch + "]");
					PullResult call = git.pull().setRemoteBranchName(gitBranch).setCredentialsProvider(usernamePasswordCredentialsProvider).call();
					log.info("Checkout meta configs OK.");
				}
			}
		}
		return gitDir;
	}
}

1、手動(dòng)觸發(fā)

	public void reloadSqlXml() {
		try {
			File mapperXmlDir = gitConfigRepository.getRepositoryDir();
			mapperHotDeployPlugin.reloadSqlXml(mapperXmlDir);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}
package com.jason.dao;

import cn.hutool.core.bean.BeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * mapper.xml熱部署,最小單位是一個(gè)xml文件
 *
 * @author: jason
 * @Date: 2022-01-13
 */


@Slf4j
@Component
public class MapperHotDeployPlugin implements InitializingBean {

	@Autowired
	private SqlSessionFactory sqlSessionFactory;

	private volatile Configuration configuration;

	@Override
	public void afterPropertiesSet() {
		configuration = sqlSessionFactory.getConfiguration();
	}

	public void reloadSqlXml(File file) {
		if (file == null) {
			return;
		}
		List<File> fileList = new ArrayList<>();
		setFiles(file, fileList, ".xml");
		reloadXml(fileList);
	}

	private void setFiles(File file, List<File> fileList, String suffix) {
		File[] files = file.listFiles();
		if (files == null || files.length == 0) {
			return;
		}
		for (File f : files) {
			if (f.isDirectory()) {
				//遞歸調(diào)用
				setFiles(f, fileList, suffix);
			} else {
				//保存文件路徑到集合中
				if (f.getAbsolutePath().contains(suffix)) {
					fileList.add(f);
				}
			}
		}
	}

	/**
	 * 重新加載sql.xml
	 *
	 * @param fileList 修改的xml資源
	 */
	private void reloadXml(List<File> fileList) {
		log.info("需要重新加載的文件列表: {}", fileList);

		fileList.forEach(r -> {
			try {
				clearMap(getNamespace(r));
				clearSet(r.getAbsolutePath());
				XMLMapperBuilder xmlMapperBuilder =
						new XMLMapperBuilder(new FileInputStream(r), getTarConfiguration(), r.toString(),
								getTarConfiguration().getSqlFragments());
				xmlMapperBuilder.parse();
			} catch (Exception e) {
				log.info("ERROR: 重新加載[{}]失敗", r.toString(), e);
				throw new RuntimeException("ERROR: 重新加載[" + r.toString() + "]失敗", e);
			} finally {
				ErrorContext.instance().reset();
			}
		});
		log.info("成功熱部署文件列表: {}", fileList);
	}

	private Configuration getTarConfiguration() {
		return configuration;
	}

	/**
	 * 刪除xml元素的節(jié)點(diǎn)緩存
	 *
	 * @param nameSpace xml中命名空間
	 */
	private void clearMap(String nameSpace) {
		log.info(
				"清理Mybatis的namespace={}在mappedStatements、caches、resultMaps、parameterMaps、keyGenerators、sqlFragments中的緩存");
		Arrays.asList("mappedStatements", "caches", "resultMaps", "parameterMaps", "keyGenerators", "sqlFragments")
				.forEach(fieldName -> {
					Object value = BeanUtil.getFieldValue(getTarConfiguration(), fieldName);
					if (value instanceof Map) {
						Map<?, ?> map = (Map) value;
						List<Object> list = map.keySet().stream().filter(o -> o.toString().startsWith(nameSpace + "."))
								.collect(Collectors.toList());
						log.info("需要清理的元素: {}", list);
						list.forEach(k -> map.remove((Object) k));
					}
				});
	}

	/**
	 * 清除文件記錄緩存
	 *
	 * @param resource xml文件路徑
	 */
	private void clearSet(String resource) {
		log.info("清理mybatis的資源{}在容器中的緩存", resource);
		Object value = BeanUtil.getFieldValue(getTarConfiguration(), "loadedResources");
		if (value instanceof Set) {
			Set<?> set = (Set) value;
			set.remove(resource);
			set.remove("namespace:" + resource);
		}
	}

	/**
	 * 獲取xml的namespace
	 *
	 * @param file xml資源
	 * @return java.lang.String
	 */
	private String getNamespace(File file) {
		try {
			XPathParser parser = new XPathParser(new FileInputStream(file), true, null, new XMLMapperEntityResolver());
			return parser.evalNode("/mapper").getStringAttribute("namespace");
		} catch (Exception e) {
			log.info("ERROR: 解析xml中namespace失敗", e);
			throw new RuntimeException("ERROR: 解析xml中namespace失敗", e);
		}
	}
}

2、自動(dòng)監(jiān)控

package com.jason.replacer.config;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * mapper.xml熱部署,最小單位是一個(gè)xml文件
 *
 * @author: jason
 * @Date: 2022-01-13
 */


@Slf4j
@Component
public class MapperHotDeployPlugin implements InitializingBean {

	@Autowired
	private SqlSessionFactory sqlSessionFactory;

	private volatile Configuration configuration;

	@Value("${mybatis.mapper-locations}")
	private String mybatisPath;

	@Override
	public void afterPropertiesSet() {
		configuration = sqlSessionFactory.getConfiguration();
		new WatchThread().start();

	}

	class WatchThread extends Thread {

		@Override
		public void run() {
			startWatch();
		}

		/**
		 * 啟動(dòng)監(jiān)聽(tīng)
		 */
		private void startWatch() {
			try {
				WatchService watcher = FileSystems.getDefault().newWatchService();
				getWatchPaths().forEach(p -> {
					try {
						Paths.get(p).register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
								StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
					} catch (Exception e) {
						log.error("ERROR: 注冊(cè)xml監(jiān)聽(tīng)事件", e);
						throw new RuntimeException("ERROR: 注冊(cè)xml監(jiān)聽(tīng)事件", e);
					}
				});
				while (true) {
					WatchKey watchKey = watcher.take();
					Set<String> set = new HashSet<>();
					for (WatchEvent<?> event : watchKey.pollEvents()) {
						set.add(event.context().toString());
					}
					// 重新加載xml
					reloadXml(set);
					boolean valid = watchKey.reset();
					if (!valid) {
						break;
					}
				}
			} catch (Exception e) {
				System.out.println("Mybatis的xml監(jiān)控失敗!");
				log.info("Mybatis的xml監(jiān)控失敗!", e);
			}
		}

		/**
		 * 加載需要監(jiān)控的文件父路徑
		 *
		 * @return java.util.Set<java.lang.String>
		 */
		private Set<String> getWatchPaths() {
			Set<String> set = new HashSet<>();
			Arrays.stream(getResource()).forEach(r -> {
				try {
					log.info("資源路徑:{}", r.toString());
					set.add(r.getFile().getParentFile().getAbsolutePath());
				} catch (Exception e) {
					log.info("獲取資源路徑失敗", e);
					throw new RuntimeException("獲取資源路徑失敗");
				}
			});
			log.info("需要監(jiān)聽(tīng)的xml資源: {}", set);
			return set;
		}

		/**
		 * 獲取配置的mapperLocations
		 *
		 * @return org.springframework.core.io.Resource[]
		 */
		@SneakyThrows
		private Resource[] getResource() {
			return new PathMatchingResourcePatternResolver().getResources(mybatisPath);
		}

		/**
		 * 刪除xml元素的節(jié)點(diǎn)緩存
		 *
		 * @param nameSpace xml中命名空間
		 */
		private void clearMap(String nameSpace) {
			log.info(
					"清理Mybatis的namespace={}在mappedStatements、caches、resultMaps、parameterMaps、keyGenerators、sqlFragments中的緩存");
			Arrays.asList("mappedStatements", "caches", "resultMaps", "parameterMaps", "keyGenerators", "sqlFragments")
					.forEach(fieldName -> {
						Object value = getFieldValue(configuration, fieldName);
						if (value instanceof Map) {
							Map<?, ?> map = (Map) value;
							List<Object> list =
									map.keySet().stream().filter(o -> o.toString().startsWith(nameSpace + "."))
											.collect(Collectors.toList());
							log.info("需要清理的元素: {}", list);
							list.forEach(k -> map.remove((Object) k));
						}
					});
		}

		/**
		 * 清除文件記錄緩存
		 *
		 * @param resource xml文件路徑
		 */
		private void clearSet(String resource) {
			log.info("清理mybatis的資源{}在容器中的緩存", resource);
			Object value = getFieldValue(configuration, "loadedResources");
			if (value instanceof Set) {
				Set<?> set = (Set) value;
				set.remove(resource);
				set.remove("namespace:" + resource);
			}
		}

		/**
		 * 獲取對(duì)象指定屬性
		 *
		 * @param obj       對(duì)象信息
		 * @param fieldName 屬性名稱
		 * @return java.lang.Object
		 */
		private Object getFieldValue(Object obj, String fieldName) {
			log.info("從{}中加載{}屬性", obj, fieldName);
			try {
				Field field = obj.getClass().getDeclaredField(fieldName);
				boolean accessible = field.isAccessible();
				field.setAccessible(true);
				Object value = field.get(obj);
				field.setAccessible(accessible);
				return value;
			} catch (Exception e) {
				log.info("ERROR: 加載對(duì)象中[{}]", fieldName, e);
				throw new RuntimeException("ERROR: 加載對(duì)象中[" + fieldName + "]", e);
			}
		}

		/**
		 * 重新加載set中xml
		 *
		 * @param set 修改的xml資源
		 */
		private void reloadXml(Set<String> set) {
			log.info("需要重新加載的文件列表: {}", set);
			List<Resource> list = Arrays.stream(getResource()).filter(p -> set.contains(p.getFilename()))
					.collect(Collectors.toList());
			log.info("需要處理的資源路徑:{}", list);
			list.forEach(r -> {
				try {
					clearMap(getNamespace(r));
					clearSet(r.toString());
					XMLMapperBuilder xmlMapperBuilder =
							new XMLMapperBuilder(r.getInputStream(), configuration, r.toString(),
									configuration.getSqlFragments());
					xmlMapperBuilder.parse();
				} catch (Exception e) {
					log.info("ERROR: 重新加載[{}]失敗", r.toString(), e);
					throw new RuntimeException("ERROR: 重新加載[" + r.toString() + "]失敗", e);
				} finally {
					ErrorContext.instance().reset();
				}
			});
			log.info("成功熱部署文件列表: {}", set);
		}

		/**
		 * 獲取xml的namespace
		 *
		 * @param resource xml資源
		 * @return java.lang.String
		 */
		private String getNamespace(Resource resource) {
			log.info("從{}獲取namespace", resource.toString());
			try {
				XPathParser parser =
						new XPathParser(resource.getInputStream(), true, null, new XMLMapperEntityResolver());
				return parser.evalNode("/mapper").getStringAttribute("namespace");
			} catch (Exception e) {
				log.info("ERROR: 解析xml中namespace失敗", e);
				throw new RuntimeException("ERROR: 解析xml中namespace失敗", e);
			}
		}
	}
}

上面提供了加載mapper.xml文件的兩種方式

讀取文件絕對(duì)路徑

	public static List<File> getFiles(String dirPath) {
		List<File> fileList = new ArrayList<>();
		File file = new File(dirPath);
		return getFiles(file, fileList, ".xml");
	}
	
	public static List<File> getFiles(File file, List<File> fileList, String suffix) {
		File[] files = file.listFiles();
		if (files == null || files.length == 0) {
			return Collections.emptyList();
		}
		for (File f : files) {
			if (f.isDirectory()) {
				//遞歸調(diào)用
				getFiles(f, fileList, suffix);
			} else {
				//保存文件路徑到集合中
				if (f.getAbsolutePath().contains(suffix)) {
					fileList.add(f);
				}
			}
		}
		return fileList;
	}

讀取classpath下的資源路徑

		@SneakyThrows
		private Resource[] getResource() {
			return new PathMatchingResourcePatternResolver().getResources("classpath:mappers/**/*.xml");
		}

總結(jié)

到此這篇關(guān)于Mybatis中mapper.xml實(shí)現(xiàn)熱加載介紹的文章就介紹到這了,更多相關(guān)Mybatis mapper.xml熱加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot項(xiàng)目中Controller接收兩個(gè)實(shí)體的實(shí)現(xiàn)方法

    SpringBoot項(xiàng)目中Controller接收兩個(gè)實(shí)體的實(shí)現(xiàn)方法

    本文主要介紹了SpringBoot項(xiàng)目中Controller接收兩個(gè)實(shí)體的實(shí)現(xiàn)方法,主要介紹了兩種方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • java.io.EOFException產(chǎn)生原因及解決方法(附代碼)

    java.io.EOFException產(chǎn)生原因及解決方法(附代碼)

    java.io.EOFException表示在讀取數(shù)據(jù)時(shí)突然遇到了文件或流的末尾,也就是說(shuō)客戶端或服務(wù)器已經(jīng)關(guān)閉了連接,但是你還在嘗試讀取數(shù)據(jù),這篇文章主要給大家介紹了關(guān)于java.io.EOFException產(chǎn)生原因及解決的相關(guān)資料,需要的朋友可以參考下
    2023-09-09
  • Java的Hibernate框架中用于操作數(shù)據(jù)庫(kù)的HQL語(yǔ)句講解

    Java的Hibernate框架中用于操作數(shù)據(jù)庫(kù)的HQL語(yǔ)句講解

    這篇文章主要介紹了Java的Hibernate框架中用于操作數(shù)據(jù)庫(kù)的HQL語(yǔ)句講解,Hibernate是Java的SSH三大web開(kāi)發(fā)框架之一,需要的朋友可以參考下
    2016-01-01
  • java工具類之實(shí)現(xiàn)java獲取文件行數(shù)

    java工具類之實(shí)現(xiàn)java獲取文件行數(shù)

    這篇文章主要介紹了一個(gè)java工具類,可以取得當(dāng)前項(xiàng)目中所有java文件總行數(shù),代碼行數(shù),注釋行數(shù),空白行數(shù),需要的朋友可以參考下
    2014-03-03
  • Spring的連接數(shù)據(jù)庫(kù)以及JDBC模板(實(shí)例講解)

    Spring的連接數(shù)據(jù)庫(kù)以及JDBC模板(實(shí)例講解)

    下面小編就為大家?guī)?lái)一篇Spring的連接數(shù)據(jù)庫(kù)以及JDBC模板(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • SpringBoot中獲取微信用戶信息的方法

    SpringBoot中獲取微信用戶信息的方法

    這篇文章主要介紹了SpringBoot中獲取微信用戶信息的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Spring注解之@Import注解的使用和源碼分析

    Spring注解之@Import注解的使用和源碼分析

    今天主要介紹Spring @Import注解,在Spring中@Import使用得比較頻繁,它得作用是導(dǎo)入bean,具體的導(dǎo)入方式有多種,特別在SpringBoot項(xiàng)目中,很多地方都使用到了@Import注解,感興趣的小伙伴可以參考閱讀
    2023-04-04
  • Java?awt-對(duì)話框簡(jiǎn)單實(shí)現(xiàn)方式

    Java?awt-對(duì)話框簡(jiǎn)單實(shí)現(xiàn)方式

    這篇文章主要介紹了Java?awt-對(duì)話框簡(jiǎn)單實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter

    dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter

    這篇文章主要介紹了dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java的RocketMq水平擴(kuò)展及負(fù)載均衡詳解

    Java的RocketMq水平擴(kuò)展及負(fù)載均衡詳解

    這篇文章主要介紹了Java的RocketMq水平擴(kuò)展及負(fù)載均衡詳解,RocketMQ是一個(gè)分布式具有高度可擴(kuò)展性的消息中間件,本文旨在探索在broker端,生產(chǎn)端,以及消費(fèi)端是如何做到橫向擴(kuò)展以及負(fù)載均衡的,需要的朋友可以參考下
    2024-01-01

最新評(píng)論