mySql 数据库热部署_实现MyBatis Mapper XML文件增量动态刷新,自动加载,热加载,热部署...

最初启动服务后Mapper XML文件,必须重启服务才能生效,这样就大大影响了我们的开发效率。

网上同学们也有实现类似功能,但都是全部清空,全部刷新XML,这样硬件消耗比较严重,加载时间也比较长。我们只修改了几行SQL就没有必要全部加载,只需要加载修改的问题就行了。

后来为了急需解决这个问题,进行修改MyBatis源码实现Mapper XML增量刷新,直接覆盖方式实现,使用classloader的加载机制优先加载,并应用到了jeesite中,但是经过MyBatis几次升级后,不得不需要重新修改,部署也麻烦,入侵性太强。

周末有幸又重新研究下源代码将刷新部分,分离出来,实现MyBatis Mapper文件动态重新加载,只加载修改的文件,今天分享出来,不多说,看源码,注释很详细:

/**

* 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线程服务。

/**

* 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, 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配置部分:

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

#是否开启刷新线程

enabled=true

#延迟启动刷新程序的秒数

delaySeconds=60

#刷新扫描间隔的时长秒数

sleepSeconds=3

#扫描Mapper文件的资源路径

mappingPath=mappings

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值