实现MyBatis Mapper XML文件增量动态刷新,自动加载,热加载,热部署
阅读更多
最初启动服务后Mapper XML文件,必须重启服务才能生效,这样就大大影响了我们的开发效率。
网上同学们也有实现类似功能,但都是全部清空,全部刷新XML,这样硬件消耗比较严重,加载时间也比较长。我们只修改了几行SQL就没有必要全部加载,只需要加载修改的问题就行了。
后来为了急需解决这个问题,进行修改MyBatis源码实现Mapper XML增量刷新,直接覆盖方式实现,使用classloader的加载机制优先加载,并应用到了jeesite中,但是经过MyBatis几次升级后,不得不需要重新修改,部署也麻烦,入侵性太强。
周末有幸又重新研究下源代码将刷新部分,分离出来,实现MyBatis Mapper文件动态重新加载,只加载修改的文件,今天分享出来,不多说,看源码,注释很详细:
-
-
-
- package com.thinkgem.jeesite.mybatis.thread;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.InputStream;
- import java.lang.reflect.Field;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Set;
-
- import org.apache.commons.lang3.StringUtils;
- import org.apache.ibatis.builder.xml.XMLMapperBuilder;
- import org.apache.ibatis.executor.ErrorContext;
- import org.apache.ibatis.session.Configuration;
- import org.apache.log4j.Logger;
- import org.springframework.core.NestedIOException;
- import org.springframework.core.io.Resource;
-
- import com.google.common.collect.Sets;
-
-
-
-
-
-
- public class MapperRefresh implements java.lang.Runnable {
-
- public static Logger log = Logger.getLogger(MapperRefresh.class);
-
- private static String filename = "/mybatis-refresh.properties";
- private static Properties prop = new Properties();
-
- private static boolean enabled;
- private static boolean refresh;
-
- private Set location;
-
- private Resource[] mapperLocations;
- private Configuration configuration;
-
- private Long beforeTime = 0L;
- private static int delaySeconds;
- private static int sleepSeconds;
- private static String mappingPath;
-
- static {
-
- try {
- prop.load(MapperRefresh.class.getResourceAsStream(filename));
- } catch (Exception e) {
- e.printStackTrace();
- System.out.println("Load mybatis-refresh “"+filename+"” file error.");
- }
-
- enabled = "true".equalsIgnoreCase(getPropString("enabled"));
-
- delaySeconds = getPropInt("delaySeconds");
- sleepSeconds = getPropInt("sleepSeconds");
- mappingPath = getPropString("mappingPath");
-
- delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;
- sleepSeconds = sleepSeconds == 0 ? 3 : sleepSeconds;
- mappingPath = StringUtils.isBlank(mappingPath) ? "mappings" : mappingPath;
-
- log.debug("[enabled] " + enabled);
- log.debug("[delaySeconds] " + delaySeconds);
- log.debug("[sleepSeconds] " + sleepSeconds);
- log.debug("[mappingPath] " + mappingPath);
- }
-
- public static boolean isRefresh() {
- return refresh;
- }
-
- public MapperRefresh(Resource[] mapperLocations, Configuration configuration) {
- this.mapperLocations = mapperLocations;
- this.configuration = configuration;
- }
-
- @Override
- public void run() {
-
- beforeTime = System.currentTimeMillis();
-
- log.debug("[location] " + location);
- log.debug("[configuration] " + configuration);
-
- if (enabled) {
-
- final MapperRefresh runnable = this;
- new Thread(new java.lang.Runnable() {
- @Override
- public void run() {
-
- if (location == null){
- location = Sets.newHashSet();
- log.debug("MapperLocation's length:" + mapperLocations.length);
- for (Resource mapperLocation : mapperLocations) {
- String s = mapperLocation.toString().replaceAll("\\\\", "/");
- s = s.substring("file [".length(), s.lastIndexOf(mappingPath) + mappingPath.length());
- if (!location.contains(s)) {
- location.add(s);
- log.debug("Location:" + s);
- }
- }
- log.debug("Locarion's size:" + location.size());
- }
-
- try {
- Thread.sleep(delaySeconds * 1000);
- } catch (InterruptedException e2) {
- e2.printStackTrace();
- }
- refresh = true;
-
- System.out.println("========= Enabled refresh mybatis mapper =========");
-
- while (true) {
- try {
- for (String s : location) {
- runnable.refresh(s, beforeTime);
- }
- } catch (Exception e1) {
- e1.printStackTrace();
- }
- try {
- Thread.sleep(sleepSeconds * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- }
- }
- }, "MyBatis-Mapper-Refresh").start();
- }
- }
-
-
-
-
-
-
-
-
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private void refresh(String filePath, Long beforeTime) throws Exception {
-
-
- Long refrehTime = System.currentTimeMillis();
-
-
- List fileList = this.getRefreshFile(new File(filePath), beforeTime);
- if (fileList.size() > 0) {
- log.debug("Refresh file: " + fileList.size());
- }
- for (int i = 0; i < fileList.size(); i++) {
- InputStream inputStream = new FileInputStream(fileList.get(i));
- String resource = fileList.get(i).getAbsolutePath();
- try {
-
-
- 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 StrictMap)){
- Map newMap = new 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) {
- throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);
- } finally {
- ErrorContext.instance().reset();
- }
- System.out.println("Refresh file: " + mappingPath + StringUtils.substringAfterLast(fileList.get(i).getAbsolutePath(), mappingPath));
- if (log.isDebugEnabled()) {
- log.debug("Refresh file: " + fileList.get(i).getAbsolutePath());
- log.debug("Refresh filename: " + fileList.get(i).getName());
- }
- }
-
- if (fileList.size() > 0) {
- this.beforeTime = refrehTime;
- }
- }
-
-
-
-
-
-
-
- private List getRefreshFile(File dir, Long beforeTime) {
- List fileList = new ArrayList();
-
- File[] files = dir.listFiles();
- if (files != null) {
- for (int i = 0; i < files.length; i++) {
- File file = files[i];
- if (file.isDirectory()) {
- fileList.addAll(this.getRefreshFile(file, beforeTime));
- } else if (file.isFile()) {
- if (this.checkFile(file, beforeTime)) {
- fileList.add(file);
- }
- } else {
- System.out.println("Error file." + file.getName());
- }
- }
- }
- return fileList;
- }
-
-
-
-
-
-
-
- private boolean checkFile(File file, Long beforeTime) {
- if (file.lastModified() > beforeTime) {
- return true;
- }
- return false;
- }
-
-
-
-
-
-
- private static int getPropInt(String key) {
- int i = 0;
- try {
- i = Integer.parseInt(getPropString(key));
- } catch (Exception e) {
- }
- return i;
- }
-
-
-
-
-
-
- private static String getPropString(String key) {
- return prop == null ? null : prop.getProperty(key);
- }
-
-
-
-
-
- public static class StrictMap extends HashMap {
-
- 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, Mapextends V> m) {
- super(m);
- this.name = name;
- }
-
- @SuppressWarnings("unchecked")
- public V put(String key, V value) {
-
- if (MapperRefresh.isRefresh()) {
- remove(key);
- MapperRefresh.log.debug("refresh key:" + key.substring(key.lastIndexOf(".") + 1));
- }
-
- 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 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 Ambiguity) {
- throw new IllegalArgumentException(((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;
- }
- }
- }
- }
/**
* Copyright (c) 2012-Now https://github.com/thinkgem/jeesite.
*/
package com.thinkgem.jeesite.mybatis.thread;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.log4j.Logger;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import com.google.common.collect.Sets;
/**
-
刷新MyBatis Mapper XML 线程
-
@author ThinkGem
-
@version 2016-5-29
*/
public class MapperRefresh implements java.lang.Runnable {
public static Logger log = Logger.getLogger(MapperRefresh.class);
private static String filename = “/mybatis-refresh.properties”;
private static Properties prop = new Properties();
private static boolean enabled; // 是否启用Mapper刷新线程功能
private static boolean refresh; // 刷新启用后,是否启动了刷新线程
private Set location; // Mapper实际资源路径
private Resource[] mapperLocations; // Mapper资源路径
private Configuration configuration; // MyBatis配置对象
private Long beforeTime = 0L; // 上一次刷新时间
private static int delaySeconds; // 延迟刷新秒数
private static int sleepSeconds; // 休眠时间
private static String mappingPath; // xml文件夹匹配字符串,需要根据需要修改
static {
try {
prop.load(MapperRefresh.class.getResourceAsStream(filename));
} catch (Exception e) {
e.printStackTrace();
System.out.println("Load mybatis-refresh “"+filename+"” file error.");
}
enabled = "true".equalsIgnoreCase(getPropString("enabled"));
delaySeconds = getPropInt("delaySeconds");
sleepSeconds = getPropInt("sleepSeconds");
mappingPath = getPropString("mappingPath");
delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;
sleepSeconds = sleepSeconds == 0 ? 3 : sleepSeconds;
mappingPath = StringUtils.isBlank(mappingPath) ? "mappings" : mappingPath;
log.debug("[enabled] " + enabled);
log.debug("[delaySeconds] " + delaySeconds);
log.debug("[sleepSeconds] " + sleepSeconds);
log.debug("[mappingPath] " + mappingPath);
}
public static boolean isRefresh() {
return refresh;
}
public MapperRefresh(Resource[] mapperLocations, Configuration configuration) {
this.mapperLocations = mapperLocations;
this.configuration = configuration;
}
@Override
public void run() {
beforeTime = System.currentTimeMillis();
log.debug("[location] " + location);
log.debug("[configuration] " + configuration);
if (enabled) {
// 启动刷新线程
final MapperRefresh runnable = this;
new Thread(new java.lang.Runnable() {
@Override
public void run() {
if (location == null){
location = Sets.newHashSet();
log.debug("MapperLocation's length:" + mapperLocations.length);
for (Resource mapperLocation : mapperLocations) {
String s = mapperLocation.toString().replaceAll("\\\\", "/");
s = s.substring("file [".length(), s.lastIndexOf(mappingPath) + mappingPath.length());
if (!location.contains(s)) {
location.add(s);
log.debug("Location:" + s);
}
}
log.debug("Locarion's size:" + location.size());
}
try {
Thread.sleep(delaySeconds * 1000);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
refresh = true;
System.out.println("========= Enabled refresh mybatis mapper =========");
while (true) {
try {
for (String s : location) {
runnable.refresh(s, beforeTime);
}
} catch (Exception e1) {
e1.printStackTrace();
}
try {
Thread.sleep(sleepSeconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "MyBatis-Mapper-Refresh").start();
}
}
/**
-
执行刷新
-
@param filePath 刷新目录
-
@param beforeTime 上次刷新时间
-
@throws NestedIOException 解析异常
-
@throws FileNotFoundException 文件未找到
-
@author ThinkGem
*/
@SuppressWarnings({ “rawtypes”, “unchecked” })
private void refresh(String filePath, Long beforeTime) throws Exception {
// 本次刷新时间
Long refrehTime = System.currentTimeMillis();
// 获取需要刷新的Mapper文件列表
List fileList = this.getRefreshFile(new File(filePath), beforeTime);
if (fileList.size() > 0) {
log.debug("Refresh file: " + fileList.size());
}
for (int i = 0; i < fileList.size(); i++) {
InputStream inputStream = new FileInputStream(fileList.get(i));
String resource = fileList.get(i).getAbsolutePath();
try {
// 清理原有资源,更新为自己的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 StrictMap)){
Map newMap = new 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) {
throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);
} finally {
ErrorContext.instance().reset();
}
System.out.println("Refresh file: " + mappingPath + StringUtils.substringAfterLast(fileList.get(i).getAbsolutePath(), mappingPath));
if (log.isDebugEnabled()) {
log.debug("Refresh file: " + fileList.get(i).getAbsolutePath());
log.debug("Refresh filename: " + fileList.get(i).getName());
}
}
// 如果刷新了文件,则修改刷新时间,否则不修改
if (fileList.size() > 0) {
this.beforeTime = refrehTime;
}
}
/**
-
获取需要刷新的文件列表
-
@param dir 目录
-
@param beforeTime 上次刷新时间
-
@return 刷新文件列表
*/
private List getRefreshFile(File dir, Long beforeTime) {
List fileList = new ArrayList();
File[] files = dir.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isDirectory()) {
fileList.addAll(this.getRefreshFile(file, beforeTime));
} else if (file.isFile()) {
if (this.checkFile(file, beforeTime)) {
fileList.add(file);
}
} else {
System.out.println(“Error file.” + file.getName());
}
}
}
return fileList;
}
/**
- 判断文件是否需要刷新
- @param file 文件
- @param beforeTime 上次刷新时间
- @return 需要刷新返回true,否则返回false
*/
private boolean checkFile(File file, Long beforeTime) {
if (file.lastModified() > beforeTime) {
return true;
}
return false;
}
/**
- 获取整数属性
- @param key
- @return
*/
private static int getPropInt(String key) {
int i = 0;
try {
i = Integer.parseInt(getPropString(key));
} catch (Exception e) {
}
return i;
}
/**
- 获取字符串属性
- @param key
- @return
*/
private static String getPropString(String key) {
return prop == null ? null : prop.getProperty(key);
}
/**
-
重写 org.apache.ibatis.session.Configuration.StrictMap 类
-
来自 MyBatis3.4.0版本,修改 put 方法,允许反复 put更新。
*/
public static class StrictMap extends HashMap {
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 m) {
super(m);
this.name = name;
}
@SuppressWarnings(“unchecked”)
public V put(String key, V value) {
// ThinkGem 如果现在状态为刷新,则刷新(先删除后添加)
if (MapperRefresh.isRefresh()) {
remove(key);
MapperRefresh.log.debug(“refresh key:” + key.substring(key.lastIndexOf(".") + 1));
}
// ThinkGem 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 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 Ambiguity) {
throw new IllegalArgumentException(((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;
}
}
}
}
MyBatis有几个不太好的地方,是当实体类别名重名的时候,Mapper XML有错误的时候,系统启动时会一直等待无法正常启动(其实是加载失败后又重新加载,进入了死循环),这里我也顺便重写下SqlSessionFactoryBean.java文件,解决这个问题,在这个文件里也加入启动上面写的线程类:
1、修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动。
2、MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动。
3、加入启动MapperRefresh.java线程服务。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- package com.thinkgem.jeesite.mybatis.spring;
-
- import static org.springframework.util.Assert.notNull;
- import static org.springframework.util.ObjectUtils.isEmpty;
- import static org.springframework.util.StringUtils.hasLength;
- import static org.springframework.util.StringUtils.tokenizeToStringArray;
-
- import java.io.IOException;
- import java.sql.SQLException;
- import java.util.Properties;
-
- import javax.sql.DataSource;
-
- import org.apache.ibatis.builder.xml.XMLConfigBuilder;
- import org.apache.ibatis.builder.xml.XMLMapperBuilder;
- import org.apache.ibatis.executor.ErrorContext;
- import org.apache.ibatis.logging.Log;
- import org.apache.ibatis.logging.LogFactory;
- import org.apache.ibatis.mapping.DatabaseIdProvider;
- import org.apache.ibatis.mapping.Environment;
- import org.apache.ibatis.plugin.Interceptor;
- import org.apache.ibatis.reflection.factory.ObjectFactory;
- import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
- import org.apache.ibatis.session.Configuration;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.apache.ibatis.session.SqlSessionFactoryBuilder;
- import org.apache.ibatis.transaction.TransactionFactory;
- import org.apache.ibatis.type.TypeHandler;
- import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
- import org.springframework.beans.factory.FactoryBean;
- import org.springframework.beans.factory.InitializingBean;
- import org.springframework.context.ApplicationEvent;
- import org.springframework.context.ApplicationListener;
- import org.springframework.context.ConfigurableApplicationContext;
- import org.springframework.context.event.ContextRefreshedEvent;
- import org.springframework.core.NestedIOException;
- import org.springframework.core.io.Resource;
- import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
-
- import com.thinkgem.jeesite.common.mybatis.thread.MapperRefresh;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {
-
- private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
-
- private Resource configLocation;
-
- private Resource[] mapperLocations;
-
- private DataSource dataSource;
-
- private TransactionFactory transactionFactory;
-
- private Properties configurationProperties;
-
- private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
-
- private SqlSessionFactory sqlSessionFactory;
-
-
- private String environment = SqlSessionFactoryBean.class.getSimpleName();
-
- private boolean failFast;
-
- private Interceptor[] plugins;
-
- private TypeHandler>[] typeHandlers;
-
- private String typeHandlersPackage;
-
- private Class>[] typeAliases;
-
- private String typeAliasesPackage;
-
- private Class> typeAliasesSuperType;
-
-
- private DatabaseIdProvider databaseIdProvider;
-
- private ObjectFactory objectFactory;
-
- private ObjectWrapperFactory objectWrapperFactory;
-
-
-
-
-
-
-
- public void setObjectFactory(ObjectFactory objectFactory) {
- this.objectFactory = objectFactory;
- }
-
-
-
-
-
-
-
- public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
- this.objectWrapperFactory = objectWrapperFactory;
- }
-
-
-
-
-
-
-
- public DatabaseIdProvider getDatabaseIdProvider() {
- return databaseIdProvider;
- }
-
-
-
-
-
-
-
-
- public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
- this.databaseIdProvider = databaseIdProvider;
- }
-
-
-
-
-
-
-
-
-
- public void setPlugins(Interceptor[] plugins) {
- this.plugins = plugins;
- }
-
-
-
-
-
-
-
-
-
- public void setTypeAliasesPackage(String typeAliasesPackage) {
- this.typeAliasesPackage = typeAliasesPackage;
- }
-
-
-
-
-
-
-
-
-
-
- public void setTypeAliasesSuperType(Class> typeAliasesSuperType) {
- this.typeAliasesSuperType = typeAliasesSuperType;
- }
-
-
-
-
-
-
-
-
-
- public void setTypeHandlersPackage(String typeHandlersPackage) {
- this.typeHandlersPackage = typeHandlersPackage;
- }
-
-
-
-
-
-
-
-
- public void setTypeHandlers(TypeHandler>[] typeHandlers) {
- this.typeHandlers = typeHandlers;
- }
-
-
-
-
-
-
-
-
- public void setTypeAliases(Class>[] typeAliases) {
- this.typeAliases = typeAliases;
- }
-
-
-
-
-
-
-
-
-
-
- public void setFailFast(boolean failFast) {
- this.failFast = failFast;
- }
-
-
-
-
-
- public void setConfigLocation(Resource configLocation) {
- this.configLocation = configLocation;
- }
-
-
-
-
-
-
-
-
-
- public void setMapperLocations(Resource[] mapperLocations) {
- this.mapperLocations = mapperLocations;
- }
-
-
-
-
-
-
- public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
- this.configurationProperties = sqlSessionFactoryProperties;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public void setDataSource(DataSource dataSource) {
- if (dataSource instanceof TransactionAwareDataSourceProxy) {
-
-
-
-
- this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
- } else {
- this.dataSource = dataSource;
- }
- }
-
-
-
-
-
-
-
-
- public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
- this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public void setTransactionFactory(TransactionFactory transactionFactory) {
- this.transactionFactory = transactionFactory;
- }
-
-
-
-
-
-
-
-
- public void setEnvironment(String environment) {
- this.environment = environment;
- }
-
-
-
-
- @Override
- public void afterPropertiesSet() throws Exception {
- notNull(dataSource, "Property 'dataSource' is required");
- notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
-
- this.sqlSessionFactory = buildSqlSessionFactory();
- }
-
-
-
-
-
-
-
-
-
-
- protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
-
- Configuration configuration;
-
- XMLConfigBuilder xmlConfigBuilder = null;
- if (this.configLocation != null) {
- xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
- configuration = xmlConfigBuilder.getConfiguration();
- } else {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
- }
- configuration = new Configuration();
- configuration.setVariables(this.configurationProperties);
- }
-
- if (this.objectFactory != null) {
- configuration.setObjectFactory(this.objectFactory);
- }
-
- if (this.objectWrapperFactory != null) {
- configuration.setObjectWrapperFactory(this.objectWrapperFactory);
- }
-
- if (hasLength(this.typeAliasesPackage)) {
- String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
- ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
- for (String packageToScan : typeAliasPackageArray) {
-
- try {
- configuration.getTypeAliasRegistry().registerAliases(packageToScan,
- typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
- } catch (Exception ex) {
- LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);
- throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);
- } finally {
- ErrorContext.instance().reset();
- }
-
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
- }
- }
- }
-
- if (!isEmpty(this.typeAliases)) {
- for (Class> typeAlias : this.typeAliases) {
- configuration.getTypeAliasRegistry().registerAlias(typeAlias);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Registered type alias: '" + typeAlias + "'");
- }
- }
- }
-
- if (!isEmpty(this.plugins)) {
- for (Interceptor plugin : this.plugins) {
- configuration.addInterceptor(plugin);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Registered plugin: '" + plugin + "'");
- }
- }
- }
-
- if (hasLength(this.typeHandlersPackage)) {
- String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
- ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
- for (String packageToScan : typeHandlersPackageArray) {
- configuration.getTypeHandlerRegistry().register(packageToScan);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
- }
- }
- }
-
- if (!isEmpty(this.typeHandlers)) {
- for (TypeHandler> typeHandler : this.typeHandlers) {
- configuration.getTypeHandlerRegistry().register(typeHandler);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Registered type handler: '" + typeHandler + "'");
- }
- }
- }
-
- if (xmlConfigBuilder != null) {
- try {
- xmlConfigBuilder.parse();
-
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
- }
- } catch (Exception ex) {
- throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
- } finally {
- ErrorContext.instance().reset();
- }
- }
-
- if (this.transactionFactory == null) {
- this.transactionFactory = new SpringManagedTransactionFactory();
- }
-
- configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
-
- if (this.databaseIdProvider != null) {
- try {
- configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
- } catch (SQLException e) {
- throw new NestedIOException("Failed getting a databaseId", e);
- }
- }
-
- if (!isEmpty(this.mapperLocations)) {
- for (Resource mapperLocation : this.mapperLocations) {
- if (mapperLocation == null) {
- continue;
- }
-
- try {
- XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
- configuration, mapperLocation.toString(), configuration.getSqlFragments());
- xmlMapperBuilder.parse();
- } catch (Exception e) {
-
- LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);
- throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
- } finally {
- ErrorContext.instance().reset();
- }
-
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
- }
- }
-
-
- new MapperRefresh(this.mapperLocations, configuration).run();
-
- } else {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
- }
- }
-
- return this.sqlSessionFactoryBuilder.build(configuration);
- }
-
-
-
-
- @Override
- public SqlSessionFactory getObject() throws Exception {
- if (this.sqlSessionFactory == null) {
- afterPropertiesSet();
- }
-
- return this.sqlSessionFactory;
- }
-
-
-
-
- @Override
- public Class extends SqlSessionFactory> getObjectType() {
- return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
- }
-
-
-
-
- @Override
- public boolean isSingleton() {
- return true;
- }
-
-
-
-
- @Override
- public void onApplicationEvent(ApplicationEvent event) {
- if (failFast && event instanceof ContextRefreshedEvent) {
-
- this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
- }
- }
-
- }
/**
* Copyright 2010-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.thinkgem.jeesite.mybatis.spring;
import static org.springframework.util.Assert.notNull;
import static org.springframework.util.ObjectUtils.isEmpty;
import static org.springframework.util.StringUtils.hasLength;
import static org.springframework.util.StringUtils.tokenizeToStringArray;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import com.thinkgem.jeesite.common.mybatis.thread.MapperRefresh;
/**
- {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}.
- This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context;
- the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection.
- Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction
- demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for transactions
- which span multiple databases or when container managed transactions (CMT) are being used.
- @author Putthibong Boonbong
- @author Hunter Presnall
- @author Eduardo Macarron
- @see #setConfigLocation
- @see #setDataSource
- @version I d Id Id
- @modify ThinkGem 2016-5-24 来自 MyBatisSpring1.2.3版本
*/
public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
private Resource configLocation;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
//EnvironmentAware requires spring 3.1
private String environment = SqlSessionFactoryBean.class.getSimpleName();
private boolean failFast;
private Interceptor[] plugins;
private TypeHandler>[] typeHandlers;
private String typeHandlersPackage;
private Class>[] typeAliases;
private String typeAliasesPackage;
private Class> typeAliasesSuperType;
//issue #19. No default provider.
private DatabaseIdProvider databaseIdProvider;
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory;
/**
- Sets the ObjectFactory.
- @since 1.1.2
- @param objectFactory
*/
public void setObjectFactory(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
/**
- Sets the ObjectWrapperFactory.
- @since 1.1.2
- @param objectWrapperFactory
*/
public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
this.objectWrapperFactory = objectWrapperFactory;
}
/**
- Gets the DatabaseIdProvider
- @since 1.1.0
- @return
*/
public DatabaseIdProvider getDatabaseIdProvider() {
return databaseIdProvider;
}
/**
- Sets the DatabaseIdProvider.
- As of version 1.2.2 this variable is not initialized by default.
- @since 1.1.0
- @param databaseIdProvider
*/
public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
this.databaseIdProvider = databaseIdProvider;
}
/**
- Mybatis plugin list.
- @since 1.0.1
- @param plugins list of plugins
*/
public void setPlugins(Interceptor[] plugins) {
this.plugins = plugins;
}
/**
- Packages to search for type aliases.
- @since 1.0.1
- @param typeAliasesPackage package to scan for domain objects
*/
public void setTypeAliasesPackage(String typeAliasesPackage) {
this.typeAliasesPackage = typeAliasesPackage;
}
/**
- Super class which domain objects have to extend to have a type alias created.
- No effect if there is no package to scan configured.
- @since 1.1.2
- @param typeAliasesSuperType super class for domain objects
*/
public void setTypeAliasesSuperType(Class> typeAliasesSuperType) {
this.typeAliasesSuperType = typeAliasesSuperType;
}
/**
- Packages to search for type handlers.
- @since 1.0.1
- @param typeHandlersPackage package to scan for type handlers
*/
public void setTypeHandlersPackage(String typeHandlersPackage) {
this.typeHandlersPackage = typeHandlersPackage;
}
/**
- Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
- @since 1.0.1
- @param typeHandlers Type handler list
*/
public void setTypeHandlers(TypeHandler>[] typeHandlers) {
this.typeHandlers = typeHandlers;
}
/**
- List of type aliases to register. They can be annotated with {@code Alias}
- @since 1.0.1
- @param typeAliases Type aliases list
*/
public void setTypeAliases(Class>[] typeAliases) {
this.typeAliases = typeAliases;
}
/**
- If true, a final check is done on Configuration to assure that all mapped
- statements are fully loaded and there is no one still pending to resolve
- includes. Defaults to false.
- @since 1.0.1
- @param failFast enable failFast
*/
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
/**
- Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
- “WEB-INF/mybatis-configuration.xml”.
*/
public void setConfigLocation(Resource configLocation) {
this.configLocation = configLocation;
}
/**
- Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}
- configuration at runtime.
- This is an alternative to specifying “<sqlmapper>” entries in an MyBatis config file.
- This property being based on Spring’s resource abstraction also allows for specifying
- resource patterns here: e.g. “classpath*:sqlmap/*-mapper.xml”.
*/
public void setMapperLocations(Resource[] mapperLocations) {
this.mapperLocations = mapperLocations;
}
/**
- Set optional properties to be passed into the SqlSession configuration, as alternative to a
- {@code <properties>} tag in the configuration xml file. This will be used to
- resolve placeholders in the config file.
*/
public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {
this.configurationProperties = sqlSessionFactoryProperties;
}
/**
- Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource}
- should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same
- JNDI DataSource for both.
- A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code
- accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.
- The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not
- a {@code TransactionAwareDataSourceProxy}. Only data access code may work with
- {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the
- underlying target {@code DataSource}. If there’s nevertheless a {@code TransactionAwareDataSourceProxy}
- passed in, it will be unwrapped to extract its target {@code DataSource}.
*/
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
// If we got a TransactionAwareDataSourceProxy, we need to perform
// transactions for its underlying target DataSource, else data
// access code won’t see properly exposed transactions (i.e.
// transactions for the target DataSource).
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
} else {
this.dataSource = dataSource;
}
}
/**
- Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}.
- This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By
- default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances.
*/
public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
}
/**
- Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}
- The default {@code SpringManagedTransactionFactory} should be appropriate for all cases:
- be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction,
- SqlSession operations will execute SQL statements non-transactionally.
- It is strongly recommended to use the default {@code TransactionFactory}. If not used, any
- attempt at getting an SqlSession through Spring’s MyBatis framework will throw an exception if
- a transaction is active.
- @see SpringManagedTransactionFactory
- @param transactionFactory the MyBatis TransactionFactory
*/
public void setTransactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
}
/**
- NOTE: This class overrides any {@code Environment} you have set in the MyBatis
- config file. This is used only as a placeholder name. The default value is
- {@code SqlSessionFactoryBean.class.getSimpleName()}.
- @param environment the environment name
*/
public void setEnvironment(String environment) {
this.environment = environment;
}
/**
- {@inheritDoc}
*/
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, “Property ‘dataSource’ is required”);
notNull(sqlSessionFactoryBuilder, “Property ‘sqlSessionFactoryBuilder’ is required”);
this.sqlSessionFactory = buildSqlSessionFactory();
}
/**
- Build a {@code SqlSessionFactory} instance.
- The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
- {@code SqlSessionFactory} instance based on an Reader.
- @return SqlSessionFactory
- @throws IOException if loading the config file failed
*/
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
// ThinkGem 修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动
try {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
} catch (Exception ex) {
LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);
throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);
} finally {
ErrorContext.instance().reset();
}
// ThinkGem end
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
// ThinkGem MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动
LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
// ThinkGem 启动刷新MapperXML定时器(有助于开发者调试)。
new MapperRefresh(this.mapperLocations, configuration).run();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
/**
- {@inheritDoc}
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
/**
- {@inheritDoc}
*/
@Override
public Class extends SqlSessionFactory> getObjectType() {
return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
}
/**
- {@inheritDoc}
*/
@Override
public boolean isSingleton() {
return true;
}
/**
- {@inheritDoc}
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
}
重写SqlSessionFactoryBean就的修改下Spring的MyBatis配置部分:
-
- <bean id="sqlSessionFactory" class="com.thinkgem.jeesite.common.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource"/>
- <property name="typeAliasesPackage" value="com.thinkgem.jeesite"/>
- <property name="typeAliasesSuperType" value="<span style="line-height: 1.5;">com.thinkgem.jeesitespan><span style="line-height: 1.5;">.persistence.BaseEntity"/>span>
- <property name="mapperLocations" value="classpath*:/mappings/**/*.xml"/>
- <property name="configLocation" value="classpath:/mybatis-config.xml">property>
- bean>
最后附加上属性配置文件:mybatis-refresh.properties
- #是否开启刷新线程
- enabled=true
- #延迟启动刷新程序的秒数
- delaySeconds=60
- #刷新扫描间隔的时长秒数
- sleepSeconds=3
- #扫描Mapper文件的资源路径
- mappingPath=mappings
#是否开启刷新线程
enabled=true
#延迟启动刷新程序的秒数
delaySeconds=60
#刷新扫描间隔的时长秒数
sleepSeconds=3
#扫描Mapper文件的资源路径
mappingPath=mappings
Java你会用,但这10点实战经验你一定不知道!
8大企业级实战项目提炼,总200+课时,限时助学2折特惠,立省4688元!
分享到:
JeeSite 4.0 规划(一) | Java如何正确地写出单例模式
- 2016-06-13 10:40
- 浏览 26246
- 评论(9)
分类:企业架构
查看更多
网上同学们也有实现类似功能,但都是全部清空,全部刷新XML,这样硬件消耗比较严重,加载时间也比较长。我们只修改了几行SQL就没有必要全部加载,只需要加载修改的问题就行了。
后来为了急需解决这个问题,进行修改MyBatis源码实现Mapper XML增量刷新,直接覆盖方式实现,
mybatis热部署加载*Mapper.xml文件,手动刷新*Mapper.xml文件
由于项目已经发布到线上,要是修改一个Mapper.xml文件的话,需要重启整个服务,这个是很耗时间的,而且在一段时间内导致服务不可用,严重影响用户
的体验度。所以希望可以有一个机制可以,当修改某个mapper.xml的时候,只要重新加载这个mapper.xml就好了,参考网上的一些资料和demo,加上一些
自己的总结,下面的代码是通过测试的,可以供你们参考和使用。
import java.i
mybatis热部署加载*Mapper.xml文件
Spring学习——手动实现Mapper.xml文件的热部署
原文转载自:https://blog.csdn.net/lovelong8808/article/details/78738086
转载使用后,根据实际情况有做部分修改,感谢原文博主提供的刷新方法。
注意,目前该方法只支持全量刷新,即刷新指定路径下的所有Mapper.xml文件,不支持仅刷新修改后的部分Mapper.xml。
由于项目已经发布到线上,要是修改一个Mapper.xm…
超简单的mybatis的mapper文件增量热部署
通常项目中如果修改mapper.xml文件 就要重启服务器才生效。网上虽然都有类似的教程,但是很多都是根本用不了.或者每次都是全量更新.本文基于http://blog.csdn.net/chunge48596/article/details/53539126?locationNum=1&fps=1的方法上更进一步优化,直接实现换包即用.
原文中对SqlSessionFactoryBean.cla
Mybatis的xml修改后自动刷新(不改源码)
思路来自于网络大神的启发,后来发现需要改源码对系统继承不太方便。尝试了一下发现mybatis已经给我们留下了足够多的可扩展点。
jrebel下载及配置(tomcat热部署)--修改java类文件、xml文件或properties资源文件自动重新加载...
Jrebel 介绍:
Jrebel 可快速实现热部署,节省了大量重启时间,提高了个人开发效率
JRebel是一款JAVA虚拟机插件,它使得JAVA程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。JRebel使你能即时分别看到代码、类和资源的变化,你可以一个个地上传而不是一次性全部部署。当程序员在开发环境中对任何一个类或者资源作出修改的时候,这个变化会直接反应在部署好…
Mybatis实现*mapper.xml热部署-分子级更新
需求:
项目在开发阶段或是修复bug阶段,会有修改mybatis的mapper.xml的时候,修改一般情况都要重启才能生失效,如果是分布式项目重启有时会耗时很久,都是无尽的等待。如果频繁修改,那么时间都浪费到等待重启的过程。
目标:
实现mybatis的mapper.xml文件修改后热部署,而且只热更新修改了的xml,可以提高重新解析过程的效率。
要求:
尽量满足开闭原则
实现:
…
针对IDEA使用JRebel热部署修改mybatis映射文件sql语句热部署失败的解决方案
本人开发环境:IDEA(2017.3) JRebel(7.X) 使用过一段时间的IDEA后感觉非常好用,并且搭配JRebel后开发简直非常舒服。但是使用的过程中有个很烦的问题,就是修改sql映射文件时热部署好像没有反应。 这个问题我最后在JRebel官网的论坛上找到了答案,大家可以参考一下。同时,也友情提示一下,也许百度或谷歌搜半天的问题,去官网的论坛里搜一下关键字一下便找到问...
Springboot整合mybatis、以及xml配置实例、热部署、打包、跳转、ssl、webapp
整合mybatis:
引入jar包,这个包是dao+server整合,内涵mybatis生成的xml,及mapper接口和bean对象
引入包后,其包的依赖也会下来,所依赖的jar
yml文件配置连接参数,数据源如果有引入jar则还可以配置数据源,mybatis配置mapper接口在哪里,需要文件路径配置/,在配置bean对象在哪里
找到启动类:
mappersc…
eclipse jrebel properties,xml等配置文件不能热部署
博主是装的jrebel8.2.4最新版本,装完后发现properties,xml等配置文件不能实现热部署,google了半天没有找到解决办法…至少到放弃的这一刻,还是没有找到办法,但是有个办法可以一定程度上解决这个问题,如下图操作:
这个办法能够将文件部署到webapp下,但是不能加载到内存中
…
SpringBoot整合Mybatis,使用通用mapper插件的时候,热部署报错,如何解决?
SpringBoot整合Mybatis,通用mapper插件热部署报错。。。。。。在使用SpringBoot 整合mybatis的时候,为了减少不必要的代码开发量,我们会使用mybatis的通用mapper插件,tk.mapper,首先引入如下的依赖:&lt;plugin&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&...
mac自动加载即热部署
idea的热部署
IDEA热部署修改mybatis映射文件工具 jr-ide-intellij-nightly.zip
IDEA热部署修改mybatis映射文件工具 jr-ide-intellij-nightly.zip
6.5热部署修改xml不重启
6.5热部署修改xml不重启
spring boot中配置mybatis热加载.zip
spring boot中配置mybatis xml资源文件热加载的方法以及相关文件
springBoot实现配置和实例的热更新,集成Apollo,方法通用
web动态部署(热部署)
今天跟大家探讨一下关于web动态部署,也就是热部署的问题。说这个之前,先说一个敏捷开发的原则。
【最小发布、增量开发】
我们在做项目时,设定的期限都特别长。总是想第一个版本就想把所有想到的问题都做完,以至于项目一再延期。所以我们应该改变我们的开发策略。采用敏捷开发的方式。
这里我想强调的有2点,1.最小发布。2.增量开发。
对于最小发布,就是要在第一版中把核心功能实现,即立
手把手教你如何玩转SpringBoot整合MyBatis(全注解开发和热部署及其JSP配置详解)
当写了前一篇关于SpringBoot的文章之后,有很多朋友就提问说,关于SpringBoot整合Mybatis,还有SpringBoot热部署,并且还有说关于整合JSP配置的一些问题,然后决定写这一篇文章来帮有疑惑的朋友来解决一下问题。(SpringBoot整合Hibernate的使用,及其SpringBoot的基本知识可以参考之前的一篇章 SpringBoot基础学习 )
…
评论