参考:http://thinkgem.iteye.com/blog/2304557

步骤:1、创建两个java类

  (1)MapperRefresh.java   :用于刷新mapper

  (2)SqlSessionFactoryBean.java :加入启动上面写的线程类,修复一些mybatis的缺陷

  (3)配置xml文件 (springmvc )或SpringBoot配置

第一步:MapperRefresh.java

/**
* 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<String> 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 == ? : delaySeconds;
sleepSeconds = sleepSeconds == ? : 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 * );
} 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 * );
} 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<File> fileList = this.getRefreshFile(new File(filePath), beforeTime);
if (fileList.size() > ) {
log.debug("Refresh file: " + fileList.size());
}
for (int i = ; 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() > ) {
this.beforeTime = refrehTime;
}
} /**
* 获取需要刷新的文件列表
* @param dir 目录
* @param beforeTime 上次刷新时间
* @return 刷新文件列表
*/
private List<File> getRefreshFile(File dir, Long beforeTime) {
List<File> fileList = new ArrayList<File>(); File[] files = dir.listFiles();
if (files != null) {
for (int i = ; 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 = ;
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<V> extends HashMap<String, V> { 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) {
// ThinkGem 如果现在状态为刷新,则刷新(先删除后添加)
if (MapperRefresh.isRefresh()) {
remove(key);
MapperRefresh.log.debug("refresh key:" + key.substring(key.lastIndexOf(".") + ));
}
// 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 - ];
} protected static class Ambiguity {
private String subject; public Ambiguity(String subject) {
this.subject = subject;
} public String getSubject() {
return subject;
}
}
}
}

第二步:SqlSessionFactoryBean.java

/**
* 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 $Id$
* @modify ThinkGem 2016-5-24 来自 MyBatisSpring1.2.3版本
*/
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { 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 "&lt;sqlmapper&gt;" 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 &lt;properties&gt;} 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.
*
* <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> 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;
} /**
* <b>NOTE:</b> This class <em>overrides</em> 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();
}
} }

最后附加上属性配置文件:mybatis-refresh.properties

#是否开启刷新线程
enabled=true
#延迟启动刷新程序的秒数
delaySeconds=
#刷新扫描间隔的时长秒数
sleepSeconds=
#扫描Mapper文件的资源路径
mappingPath=mappings

第三步:配置扫描路径

SpringMVC 和SpringBoot 都是需要配置扫描XML的路径,和mybatis的配置文件,只不过SpringMVC和Springboot配置的方式不同。

(1)SpringMVC 方式

<!-- MyBatis SqlSessionFactoryBean -->
<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="mapperLocations" value="classpath*:/mappings/**/*.xml"/>
<property name="configLocation" value="classpath:/mybatis-config.xml"></property>
</bean>

(2)SpringBoot 方式

@Configuration
@MapperScan(basePackages="com.oskyhang.*.mapper", sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class DatasourceConfig { @Bean(name = "test1DataSource")
@ConfigurationProperties(prefix = "spring.datasource")
@Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
} @Bean(name = "test1SqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// TODO
// 加载全局的配置文件
bean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:sqlMapConfig.xml")); Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:com/oskyhang/system/mapper/*.xml");
bean.setMapperLocations(resources); return bean.getObject();
} @Bean(name = "test1TransactionManager")
@Primary
public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
} @Bean(name = "test1SqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
} }

mybatis 热部署xml文件(spring boot和springmvc两种方式)的更多相关文章

  1. Java中将xml文件转化为json的两种方式

    原文地址https://blog.csdn.net/a532672728/article/details/76312475 最近有个需求,要将xml转json之后存储在redis中,找来找去发现整体来 ...

  2. 在CentOS下的docker容器中部署spring boot应用的两种方式

    我们通常在 windows 环境下开发 Java,而通常是部署在Linux的服务器中,而CentOS通常是大多数企业的首选,基于Docker的虚拟化容器技术,多数Java应用选择这种方式部署服务.本文 ...

  3. spring boot集成pagehelper(两种方式)

    当spring boot集成好mybatis时候需要进行分页,我们首先添加maven支持 <dependency> <groupId>com.github.pagehelper ...

  4. 运行 Spring Boot 应用的 3 种方式

    今天介绍 3 种运行 Spring Boot 应用的方式,看大家用过几种? 你所需具备的基础 什么是 Spring Boot? Spring Boot 核心配置文件详解 Spring Boot 开启的 ...

  5. Spring创建JobDetail的两种方式

    一.Spring创建JobDetail的两种方式 二.整合方式一示例步骤 1.将spring核心jar包.quartz.jar和Spring-context-support.jar导入类路径. 2.编 ...

  6. spring配置属性的两种方式

    spring配置属性有两种方式,第一种方式通过context命名空间中的property-placeholder标签 <context:property-placeholder location ...

  7. web.config文件中配置数据库连接的两种方式

    web.config文件中配置数据库连接的两种方式 标签: 数据库webconfig 2015-04-28 18:18 31590人阅读 评论(1)收藏举报    分类: 数据库(74)  在网站开发 ...

  8. Django学习——ajax发送其他请求、上传文件(ajax和form两种方式)、ajax上传json格式、 Django内置序列化(了解)、分页器的使用

    1 ajax发送其他请求 1 写在form表单 submit和button会触发提交 <form action=""> </form> 注释 2 使用inp ...

  9. Spring Boot开启的 2 种方式

    Spring Boot依赖 使用Spring Boot很简单,先添加基础依赖包,有以下两种方式 1. 继承spring-boot-starter-parent项目 <parent> < ...

随机推荐

  1. sql server存储特殊字符解决办法

    好久没来院子了,最近在学java了,再加上项目比较紧,最近都没怎么上,其实这几天在项目中学到不少东西,都能写下来,但是久而久之就忘了,还是得养成及时总结的好习惯啊,还有有时间一定要把那个小项目整理下来 ...

  2. 开源自动驾驶仿真平台 AirSim (3) - 运行 AirSim

    AirSim 的官方 Github: https://github.com/Microsoft/AirSim 之前配置了很多,终于要让 AirSim 自己跑起来了. 我们需要把 AirSim 这个插件 ...

  3. 软件工程第四周作业之四则运算-C#实现

    拿到题目的时候,快放假了,也没心思做.十月七号的一下午大概从两点做到八点半,加上十月八号的十二点半到两点半,做了一共八个半小时,去掉吃饭半个小时那么一共做了八个小时. 逆波兰表达式我是扒的别人代码,没 ...

  4. 软件工程-东北师大站-第二次作业psp

    1.本周PSP 2.本周进度条 3.本周累计进度图 代码累计折线图 博文字数累计折线图 本周PSP饼状图

  5. 20145214实验五 Java网络编程及安全

    20145214实验五 Java网络编程及安全 实验内容 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.设计安全传输系统. 实验步骤 我的结对伙伴是 20145219 宋歌,我负责的 ...

  6. POJ 2229 计数DP

    dp[i]代表是数字i的最多组合数如果i是一个奇数,i的任意一个组合都包含1,所以dp[i] = dp[i-1] 如果i是一个偶数,分两种情况讨论,一种是序列中包含1,因此dp[i]=dp[i-1]一 ...

  7. UVALive - 6872 Restaurant Ratings 数位dp

    题目链接: http://acm.hust.edu.cn/vjudge/problem/113727 Restaurant Ratings Time Limit: 3000MS 题意 给你一个长度为n ...

  8. lintcode-152-组合

    152-组合 组给出两个整数n和k,返回从1......n中选出的k个数的组合. 样例 例如 n = 4 且 k = 2 返回的解为: [[2,4],[3,4],[2,3],[1,2],[1,3],[ ...

  9. Windows网络编程系列教程之四:Select模型

    讲一下套接字模式和套接字I/O模型的区别.先说明一下,只针对Winsock,如果你要骨头里挑鸡蛋把UNIX下的套接字概念来往这里套,那就不关我的事. 套接字模式:阻塞套接字和非阻塞套接字.或者叫同步套 ...

  10. iOS- 封装单例宏

    在项目中,我们需要全局只有一个实例,节省不必要的内存,这时我们就需要使用里单例生成对象. 这时把单例的代码封装成宏,就能方便我们下次使用了. 在.h .m里直接导入头文件,调用 传入类名即可! sin ...