首页 技术 正文
技术 2022年11月8日
0 收藏 554 点赞 1,469 浏览 60887 个字

  由于在本地开发环境上每次修改mybatis xml文件都需要手动重启服务,调试的很麻烦,所以需要热加载xml文件来避免浪费时间,于是网上搜一下资料,看了下有一大堆,但试了下真正能跑起来没有(大都代码没给全),故参考了改了下。

首先确定 mybatis 修改的类:

  XMLMapperBuilder(默认xml加载后不会再次加载了,修改此逻辑)
  Configuration(重写StrictMap put方法,删除原来加载的数据,重新加载)
  SqlSessionFactoryBean(启动刷新线程) 1、在 src 下面建立与 myabtis 上述相同目录的类(tomcat 启动的时候会先加载classes目录下的文件,然后才会加载lib下面的jar包,根据类加载机制,加载了src下面同目录后的类就不会再加载jar包里面的类了)。
 2、代码如下:
/*
* Copyright 2010-2013 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 org.mybatis.spring;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.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.util.ClassUtils;import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;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;/**
* {@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$
* @desctiption 刷新xml文件
*/
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; private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1 private boolean failFast; private Interceptor[] plugins; private TypeHandler<?>[] typeHandlers; private String typeHandlersPackage; private Class<?>[] typeAliases; private String typeAliasesPackage; private Class<?> typeAliasesSuperType; private DatabaseIdProvider databaseIdProvider; // issue #19. No default provider. 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)) {
// TODO 支持自定义通配符
String[] typeAliasPackageArray;
if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",")
&& !typeAliasesPackage.contains(";")) {
typeAliasPackageArray = convertTypeAliasesPackage(typeAliasesPackage);
} else {
typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
}
if (typeAliasPackageArray == null) {
throw new IOException("not find typeAliasesPackage:" + typeAliasesPackage);
}
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
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();
} Environment environment = new Environment(this.environment,
this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment); if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider
.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
String location = null;
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (location == null && mapperLocation instanceof FileSystemResource) {
location = mapperLocation.toString();
}
if (mapperLocation == null) {
continue;
} try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
mapperLocation.getInputStream(), configuration,
mapperLocation.toString(),
configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
e.printStackTrace(); // 出现错误抛出异常
throw new NestedIOException(
"Failed to parse mapping resource: '"
+ mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
} if (logger.isDebugEnabled()) {
logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
} //启动定时器
new org.apache.ibatis.thread.RefreshRunnable(mapperLocations, configuration).run(); return this.sqlSessionFactoryBuilder.build(configuration);
} /**
* {@inheritDoc}
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
} return this.sqlSessionFactory;
} /**
* {@inheritDoc}
*/
public Class<? extends SqlSessionFactory> getObjectType() {
return this.sqlSessionFactory == null ? SqlSessionFactory.class
: this.sqlSessionFactory.getClass();
} /**
* {@inheritDoc}
*/
public boolean isSingleton() {
return true;
} /**
* {@inheritDoc}
*/
public void onApplicationEvent(ApplicationEvent event) {
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
} /**
* 重新加载 xmlMapper
* @param inputStream
* @param resource
* @param configuration
* @throws NestedIOException
*/
public static void refresh(java.io.InputStream inputStream,
String resource, Configuration configuration)
throws NestedIOException {
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
inputStream, configuration, resource,
configuration.getSqlFragments());
xmlMapperBuilder.parse1();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '"
+ resource + "'", e);
} finally {
ErrorContext.instance().reset();
} } public static String[] convertTypeAliasesPackage(String typeAliasesPackage) throws IOException{
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
String pkg = "classpath*:" + ClassUtils.convertClassNameToResourcePath(typeAliasesPackage) + "/*.class"; try {
Set<String> set = new HashSet<>();
Resource[] resources = resolver.getResources(pkg);
if(resources != null && resources.length > 0) {
Resource[] arr = resources;
int len = resources.length; for(int i = 0; i < len; ++i) {
Resource resource = arr[i];
if(resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
set.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
}
}
} if(!set.isEmpty()) {
return set.toArray(new String[0]);
} else {
throw new IOException("not find typeAliasesPackage:" + pkg);
}
} catch (Exception var11) {
throw new IOException("not find typeAliasesPackage:" + pkg);
}
}}
/*
* Copyright 2009-2014 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 org.apache.ibatis.session;import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.builder.CacheRefResolver;
import org.apache.ibatis.builder.ResultMapResolver;
import org.apache.ibatis.builder.annotation.MethodResolver;
import org.apache.ibatis.builder.xml.XMLStatementBuilder;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.decorators.FifoCache;
import org.apache.ibatis.cache.decorators.LruCache;
import org.apache.ibatis.cache.decorators.SoftCache;
import org.apache.ibatis.cache.decorators.WeakCache;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import org.apache.ibatis.executor.*;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.loader.ProxyFactory;
import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.InterceptorChain;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.LanguageDriverRegistry;
import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.apache.ibatis.type.TypeHandlerRegistry;import java.util.*;/**
* @author Clinton Begin
* @description 重写put,实现刷新的功能
*/
public class Configuration { protected Environment environment; protected boolean safeRowBoundsEnabled = false;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase = false;
protected boolean aggressiveLazyLoading = true;
protected boolean multipleResultSetsEnabled = true;
protected boolean useGeneratedKeys = false;
protected boolean useColumnLabel = true;
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls = false;
protected String logPrefix;
protected Class<? extends Log> logImpl;
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(
Arrays.asList("equals", "clone", "hashCode",
"toString"));
protected Integer defaultStatementTimeout;
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL; protected Properties variables = new Properties();
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
protected MapperRegistry mapperRegistry = new MapperRegistry(this); protected boolean lazyLoadingEnabled = false;
protected ProxyFactory proxyFactory; protected String databaseId;
/**
* Configuration factory class. Used to create Configuration for loading
* deserialized unread properties.
*
* @see <a
* href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue
* 300</a> (google code)
*/
protected Class<?> configurationFactory; protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry(); protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
"Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>(
"Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>(
"Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>(
"Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>(
"Key Generators collection"); protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>(
"XML fragments parsed from previous mappers"); protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>(); /*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<String, String>(); public Configuration(Environment environment) {
this();
this.environment = environment;
} public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED",
ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry
.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED",
UnpooledDataSourceFactory.class); typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class); typeAliasRegistry.registerAlias("DB_VENDOR",
VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING",
JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST",
JavassistProxyFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
} public String getLogPrefix() {
return logPrefix;
} public void setLogPrefix(String logPrefix) {
this.logPrefix = logPrefix;
} public Class<? extends Log> getLogImpl() {
return logImpl;
} @SuppressWarnings("unchecked")
public void setLogImpl(Class<?> logImpl) {
if (logImpl != null) {
this.logImpl = (Class<? extends Log>) logImpl;
LogFactory.useCustomLogging(this.logImpl);
}
} public boolean isCallSettersOnNulls() {
return callSettersOnNulls;
} public void setCallSettersOnNulls(boolean callSettersOnNulls) {
this.callSettersOnNulls = callSettersOnNulls;
} public String getDatabaseId() {
return databaseId;
} public void setDatabaseId(String databaseId) {
this.databaseId = databaseId;
} public Class<?> getConfigurationFactory() {
return configurationFactory;
} public void setConfigurationFactory(Class<?> configurationFactory) {
this.configurationFactory = configurationFactory;
} public boolean isSafeResultHandlerEnabled() {
return safeResultHandlerEnabled;
} public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
this.safeResultHandlerEnabled = safeResultHandlerEnabled;
} public boolean isSafeRowBoundsEnabled() {
return safeRowBoundsEnabled;
} public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) {
this.safeRowBoundsEnabled = safeRowBoundsEnabled;
} public boolean isMapUnderscoreToCamelCase() {
return mapUnderscoreToCamelCase;
} public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
} public void addLoadedResource(String resource) {
loadedResources.add(resource);
} public boolean isResourceLoaded(String resource) {
return loadedResources.contains(resource);
} public Environment getEnvironment() {
return environment;
} public void setEnvironment(Environment environment) {
this.environment = environment;
} public AutoMappingBehavior getAutoMappingBehavior() {
return autoMappingBehavior;
} public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) {
this.autoMappingBehavior = autoMappingBehavior;
} public boolean isLazyLoadingEnabled() {
return lazyLoadingEnabled;
} public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
this.lazyLoadingEnabled = lazyLoadingEnabled;
} public ProxyFactory getProxyFactory() {
if (proxyFactory == null) {
// makes sure CGLIB is not needed unless explicitly requested
proxyFactory = new CglibProxyFactory();
}
return proxyFactory;
} public void setProxyFactory(ProxyFactory proxyFactory) {
this.proxyFactory = proxyFactory;
} public boolean isAggressiveLazyLoading() {
return aggressiveLazyLoading;
} public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) {
this.aggressiveLazyLoading = aggressiveLazyLoading;
} public boolean isMultipleResultSetsEnabled() {
return multipleResultSetsEnabled;
} public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
this.multipleResultSetsEnabled = multipleResultSetsEnabled;
} public Set<String> getLazyLoadTriggerMethods() {
return lazyLoadTriggerMethods;
} public void setLazyLoadTriggerMethods(Set<String> lazyLoadTriggerMethods) {
this.lazyLoadTriggerMethods = lazyLoadTriggerMethods;
} public boolean isUseGeneratedKeys() {
return useGeneratedKeys;
} public void setUseGeneratedKeys(boolean useGeneratedKeys) {
this.useGeneratedKeys = useGeneratedKeys;
} public ExecutorType getDefaultExecutorType() {
return defaultExecutorType;
} public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
this.defaultExecutorType = defaultExecutorType;
} public boolean isCacheEnabled() {
return cacheEnabled;
} public void setCacheEnabled(boolean cacheEnabled) {
this.cacheEnabled = cacheEnabled;
} public Integer getDefaultStatementTimeout() {
return defaultStatementTimeout;
} public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
this.defaultStatementTimeout = defaultStatementTimeout;
} public boolean isUseColumnLabel() {
return useColumnLabel;
} public void setUseColumnLabel(boolean useColumnLabel) {
this.useColumnLabel = useColumnLabel;
} public LocalCacheScope getLocalCacheScope() {
return localCacheScope;
} public void setLocalCacheScope(LocalCacheScope localCacheScope) {
this.localCacheScope = localCacheScope;
} public JdbcType getJdbcTypeForNull() {
return jdbcTypeForNull;
} public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
this.jdbcTypeForNull = jdbcTypeForNull;
} public Properties getVariables() {
return variables;
} public void setVariables(Properties variables) {
this.variables = variables;
} public TypeHandlerRegistry getTypeHandlerRegistry() {
return typeHandlerRegistry;
} public TypeAliasRegistry getTypeAliasRegistry() {
return typeAliasRegistry;
} /**
* @since 3.2.2
*/
public MapperRegistry getMapperRegistry() {
return mapperRegistry;
} public ObjectFactory getObjectFactory() {
return objectFactory;
} public void setObjectFactory(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
} public ObjectWrapperFactory getObjectWrapperFactory() {
return objectWrapperFactory;
} public void setObjectWrapperFactory(
ObjectWrapperFactory objectWrapperFactory) {
this.objectWrapperFactory = objectWrapperFactory;
} /**
* @since 3.2.2
*/
public List<Interceptor> getInterceptors() {
return interceptorChain.getInterceptors();
} public LanguageDriverRegistry getLanguageRegistry() {
return languageRegistry;
} public void setDefaultScriptingLanguage(Class<?> driver) {
if (driver == null) {
driver = XMLLanguageDriver.class;
}
getLanguageRegistry().setDefaultDriverClass(driver);
} public LanguageDriver getDefaultScriptingLanuageInstance() {
return languageRegistry.getDefaultDriver();
} public MetaObject newMetaObject(Object object) {
return MetaObject
.forObject(object, objectFactory, objectWrapperFactory);
} public ParameterHandler newParameterHandler(
MappedStatement mappedStatement, Object parameterObject,
BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang()
.createParameterHandler(mappedStatement, parameterObject,
boundSql);
parameterHandler = (ParameterHandler) interceptorChain
.pluginAll(parameterHandler);
return parameterHandler;
} public ResultSetHandler newResultSetHandler(Executor executor,
MappedStatement mappedStatement, RowBounds rowBounds,
ParameterHandler parameterHandler, ResultHandler resultHandler,
BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(
executor, mappedStatement, parameterHandler, resultHandler,
boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain
.pluginAll(resultSetHandler);
return resultSetHandler;
} public StatementHandler newStatementHandler(Executor executor,
MappedStatement mappedStatement, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(
executor, mappedStatement, parameterObject, rowBounds,
resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain
.pluginAll(statementHandler);
return statementHandler;
} public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
} public Executor newExecutor(Transaction transaction,
ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType
: executorType;
executorType = executorType == null ? ExecutorType.SIMPLE
: executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
} public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
keyGenerators.put(id, keyGenerator);
} public Collection<String> getKeyGeneratorNames() {
return keyGenerators.keySet();
} public Collection<KeyGenerator> getKeyGenerators() {
return keyGenerators.values();
} public KeyGenerator getKeyGenerator(String id) {
return keyGenerators.get(id);
} public boolean hasKeyGenerator(String id) {
return keyGenerators.containsKey(id);
} public void addCache(Cache cache) {
caches.put(cache.getId(), cache);
} public Collection<String> getCacheNames() {
return caches.keySet();
} public Collection<Cache> getCaches() {
return caches.values();
} public Cache getCache(String id) {
return caches.get(id);
} public boolean hasCache(String id) {
return caches.containsKey(id);
} public void addResultMap(ResultMap rm) {
resultMaps.put(rm.getId(), rm);
checkLocallyForDiscriminatedNestedResultMaps(rm);
checkGloballyForDiscriminatedNestedResultMaps(rm);
} public Collection<String> getResultMapNames() {
return resultMaps.keySet();
} public Collection<ResultMap> getResultMaps() {
return resultMaps.values();
} public ResultMap getResultMap(String id) {
return resultMaps.get(id);
} public boolean hasResultMap(String id) {
return resultMaps.containsKey(id);
} public void addParameterMap(ParameterMap pm) {
parameterMaps.put(pm.getId(), pm);
} public Collection<String> getParameterMapNames() {
return parameterMaps.keySet();
} public Collection<ParameterMap> getParameterMaps() {
return parameterMaps.values();
} public ParameterMap getParameterMap(String id) {
return parameterMaps.get(id);
} public boolean hasParameterMap(String id) {
return parameterMaps.containsKey(id);
} public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
} public Collection<String> getMappedStatementNames() {
buildAllStatements();
return mappedStatements.keySet();
} public Collection<MappedStatement> getMappedStatements() {
buildAllStatements();
return mappedStatements.values();
} public Collection<XMLStatementBuilder> getIncompleteStatements() {
return incompleteStatements;
} public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
incompleteStatements.add(incompleteStatement);
} public Collection<CacheRefResolver> getIncompleteCacheRefs() {
return incompleteCacheRefs;
} public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
incompleteCacheRefs.add(incompleteCacheRef);
} public Collection<ResultMapResolver> getIncompleteResultMaps() {
return incompleteResultMaps;
} public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
incompleteResultMaps.add(resultMapResolver);
} public void addIncompleteMethod(MethodResolver builder) {
incompleteMethods.add(builder);
} public Collection<MethodResolver> getIncompleteMethods() {
return incompleteMethods;
} public MappedStatement getMappedStatement(String id) {
return this.getMappedStatement(id, true);
} public MappedStatement getMappedStatement(String id,
boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
} public Map<String, XNode> getSqlFragments() {
return sqlFragments;
} public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
} public void addMappers(String packageName, Class<?> superType) {
mapperRegistry.addMappers(packageName, superType);
} public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
} public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
} public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
} public boolean hasMapper(Class<?> type) {
return mapperRegistry.hasMapper(type);
} public boolean hasStatement(String statementName) {
return hasStatement(statementName, true);
} public boolean hasStatement(String statementName,
boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.containsKey(statementName);
} public void addCacheRef(String namespace, String referencedNamespace) {
cacheRefMap.put(namespace, referencedNamespace);
} /*
* Parses all the unprocessed statement nodes in the cache. It is
* recommended to call this method once all the mappers are added as it
* provides fail-fast statement validation.
*/
protected void buildAllStatements() {
if (!incompleteResultMaps.isEmpty()) {
synchronized (incompleteResultMaps) {
// This always throws a BuilderException.
incompleteResultMaps.iterator().next().resolve();
}
}
if (!incompleteCacheRefs.isEmpty()) {
synchronized (incompleteCacheRefs) {
// This always throws a BuilderException.
incompleteCacheRefs.iterator().next().resolveCacheRef();
}
}
if (!incompleteStatements.isEmpty()) {
synchronized (incompleteStatements) {
// This always throws a BuilderException.
incompleteStatements.iterator().next().parseStatementNode();
}
}
if (!incompleteMethods.isEmpty()) {
synchronized (incompleteMethods) {
// This always throws a BuilderException.
incompleteMethods.iterator().next().resolve();
}
}
} /*
* Extracts namespace from fully qualified statement id.
*
* @param statementId
*
* @return namespace or null when id does not contain period.
*/
protected String extractNamespace(String statementId) {
int lastPeriod = statementId.lastIndexOf('.');
return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
} // Slow but a one time cost. A better solution is welcome.
protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
if (rm.hasNestedResultMaps()) {
for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
Object value = entry.getValue();
if (value instanceof ResultMap) {
ResultMap entryResultMap = (ResultMap) value;
if (!entryResultMap.hasNestedResultMaps()
&& entryResultMap.getDiscriminator() != null) {
Collection<String> discriminatedResultMapNames = entryResultMap
.getDiscriminator().getDiscriminatorMap()
.values();
if (discriminatedResultMapNames.contains(rm.getId())) {
entryResultMap.forceNestedResultMaps();
}
}
}
}
}
} // Slow but a one time cost. A better solution is welcome.
protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
for (Map.Entry<String, String> entry : rm.getDiscriminator()
.getDiscriminatorMap().entrySet()) {
String discriminatedResultMapName = entry.getValue();
if (hasResultMap(discriminatedResultMapName)) {
ResultMap discriminatedResultMap = resultMaps
.get(discriminatedResultMapName);
if (discriminatedResultMap.hasNestedResultMaps()) {
rm.forceNestedResultMaps();
break;
}
}
}
}
}
  
protected 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) {
if (org.apache.ibatis.thread.RefreshRunnable.isRefresh()) {
remove(key);
org.apache.ibatis.thread.RefreshRunnable.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("\\.");
final String shortKey = keyparts[keyparts.length - 1];
return shortKey;
} protected static class Ambiguity {
private String subject; public Ambiguity(String subject) {
this.subject = subject;
} public String getSubject() {
return subject;
}
}
}}
/*
* Copyright 2009-2013 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 org.apache.ibatis.builder.xml;import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.CacheRefResolver;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.builder.ResultMapResolver;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Discriminator;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.ResultFlag;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;/**
* @author Clinton Begin
* @description 增加解析方法,原方法不解析相同的resource
*/
public class XMLMapperBuilder extends BaseBuilder { private XPathParser parser;
private MapperBuilderAssistant builderAssistant;
private Map<String, XNode> sqlFragments;
private String resource; @Deprecated
public XMLMapperBuilder(Reader reader, Configuration configuration,
String resource, Map<String, XNode> sqlFragments, String namespace) {
this(reader, configuration, resource, sqlFragments);
this.builderAssistant.setCurrentNamespace(namespace);
} @Deprecated
public XMLMapperBuilder(Reader reader, Configuration configuration,
String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(reader, true, configuration.getVariables(),
new XMLMapperEntityResolver()), configuration, resource,
sqlFragments);
} public XMLMapperBuilder(InputStream inputStream,
Configuration configuration, String resource,
Map<String, XNode> sqlFragments, String namespace) {
this(inputStream, configuration, resource, sqlFragments);
this.builderAssistant.setCurrentNamespace(namespace);
} public XMLMapperBuilder(InputStream inputStream,
Configuration configuration, String resource,
Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(),
new XMLMapperEntityResolver()), configuration, resource,
sqlFragments);
} private XMLMapperBuilder(XPathParser parser, Configuration configuration,
String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration,
resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
} public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
} parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
} public void parse1() {
// if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
// } parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
} public XNode getSqlFragment(String refid) {
return sqlFragments.get(refid);
} private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context
.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e,
e);
}
} private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
} private void buildStatementFromContext(List<XNode> list,
String requiredDatabaseId) {
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(
configuration, builderAssistant, context,
requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
} private void parsePendingResultMaps() {
Collection<ResultMapResolver> incompleteResultMaps = configuration
.getIncompleteResultMaps();
synchronized (incompleteResultMaps) {
Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
while (iter.hasNext()) {
try {
iter.next().resolve();
iter.remove();
} catch (IncompleteElementException e) {
// ResultMap is still missing a resource...
}
}
}
} private void parsePendingChacheRefs() {
Collection<CacheRefResolver> incompleteCacheRefs = configuration
.getIncompleteCacheRefs();
synchronized (incompleteCacheRefs) {
Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
while (iter.hasNext()) {
try {
iter.next().resolveCacheRef();
iter.remove();
} catch (IncompleteElementException e) {
// Cache ref is still missing a resource...
}
}
}
} private void parsePendingStatements() {
Collection<XMLStatementBuilder> incompleteStatements = configuration
.getIncompleteStatements();
synchronized (incompleteStatements) {
Iterator<XMLStatementBuilder> iter = incompleteStatements
.iterator();
while (iter.hasNext()) {
try {
iter.next().parseStatementNode();
iter.remove();
} catch (IncompleteElementException e) {
// Statement is still missing a resource...
}
}
}
} private void cacheRefElement(XNode context) {
if (context != null) {
configuration.addCacheRef(builderAssistant.getCurrentNamespace(),
context.getStringAttribute("namespace"));
CacheRefResolver cacheRefResolver = new CacheRefResolver(
builderAssistant, context.getStringAttribute("namespace"));
try {
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
} private void cacheElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry
.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry
.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass,
flushInterval, size, readWrite, props);
}
} private void parameterMapElement(List<XNode> list) throws Exception {
for (XNode parameterMapNode : list) {
String id = parameterMapNode.getStringAttribute("id");
String type = parameterMapNode.getStringAttribute("type");
Class<?> parameterClass = resolveClass(type);
List<XNode> parameterNodes = parameterMapNode
.evalNodes("parameter");
List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
for (XNode parameterNode : parameterNodes) {
String property = parameterNode.getStringAttribute("property");
String javaType = parameterNode.getStringAttribute("javaType");
String jdbcType = parameterNode.getStringAttribute("jdbcType");
String resultMap = parameterNode
.getStringAttribute("resultMap");
String mode = parameterNode.getStringAttribute("mode");
String typeHandler = parameterNode
.getStringAttribute("typeHandler");
Integer numericScale = parameterNode
.getIntAttribute("numericScale");
ParameterMode modeEnum = resolveParameterMode(mode);
Class<?> javaTypeClass = resolveClass(javaType);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
ParameterMapping parameterMapping = builderAssistant
.buildParameterMapping(parameterClass, property,
javaTypeClass, jdbcTypeEnum, resultMap,
modeEnum, typeHandlerClass, numericScale);
parameterMappings.add(parameterMapping);
}
builderAssistant.addParameterMap(id, parameterClass,
parameterMappings);
}
} private void resultMapElements(List<XNode> list) throws Exception {
for (XNode resultMapNode : list) {
try {
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
} private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode,
Collections.<ResultMapping> emptyList());
} private ResultMap resultMapElement(XNode resultMapNode,
List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity(
"processing " + resultMapNode.getValueBasedIdentifier());
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String type = resultMapNode.getStringAttribute("type", resultMapNode
.getStringAttribute("ofType", resultMapNode.getStringAttribute(
"resultType",
resultMapNode.getStringAttribute("javaType"))));
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass,
resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild,
typeClass, resultMappings);
} else {
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild,
typeClass, flags));
}
}
ResultMapResolver resultMapResolver = new ResultMapResolver(
builderAssistant, id, typeClass, extend, discriminator,
resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
} private void processConstructorElement(XNode resultChild,
Class<?> resultType, List<ResultMapping> resultMappings)
throws Exception {
List<XNode> argChildren = resultChild.getChildren();
for (XNode argChild : argChildren) {
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
flags.add(ResultFlag.CONSTRUCTOR);
if ("idArg".equals(argChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(argChild,
resultType, flags));
}
} private Discriminator processDiscriminatorElement(XNode context,
Class<?> resultType, List<ResultMapping> resultMappings)
throws Exception {
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String typeHandler = context.getStringAttribute("typeHandler");
Class<?> javaTypeClass = resolveClass(javaType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
Map<String, String> discriminatorMap = new HashMap<String, String>();
for (XNode caseChild : context.getChildren()) {
String value = caseChild.getStringAttribute("value");
String resultMap = caseChild.getStringAttribute("resultMap",
processNestedResultMappings(caseChild, resultMappings));
discriminatorMap.put(value, resultMap);
}
return builderAssistant
.buildDiscriminator(resultType, column, javaTypeClass,
jdbcTypeEnum, typeHandlerClass, discriminatorMap);
} private void sqlElement(List<XNode> list) throws Exception {
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
} private void sqlElement(List<XNode> list, String requiredDatabaseId)
throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId))
sqlFragments.put(id, context);
}
} private boolean databaseIdMatchesCurrent(String id, String databaseId,
String requiredDatabaseId) {
if (requiredDatabaseId != null) {
if (!requiredDatabaseId.equals(databaseId)) {
return false;
}
} else {
if (databaseId != null) {
return false;
}
// skip this fragment if there is a previous one with a not null
// databaseId
if (this.sqlFragments.containsKey(id)) {
XNode context = this.sqlFragments.get(id);
if (context.getStringAttribute("databaseId") != null) {
return false;
}
}
}
return true;
} private ResultMapping buildResultMappingFromContext(XNode context,
Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
String property = context.getStringAttribute("property");
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute(
"resultMap",
processNestedResultMappings(context,
Collections.<ResultMapping> emptyList()));
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resulSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType",
configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
Class<?> javaTypeClass = resolveClass(javaType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
return builderAssistant.buildResultMapping(resultType, property,
column, javaTypeClass, jdbcTypeEnum, nestedSelect,
nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass,
flags, resulSet, foreignColumn, lazy);
} private String processNestedResultMappings(XNode context,
List<ResultMapping> resultMappings) throws Exception {
if ("association".equals(context.getName())
|| "collection".equals(context.getName())
|| "case".equals(context.getName())) {
if (context.getStringAttribute("select") == null) {
ResultMap resultMap = resultMapElement(context, resultMappings);
return resultMap.getId();
}
}
return null;
} private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a
// flag
// to prevent loading again this resource from the mapper
// interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}}

增加刷新xml的线程(核心配置):

package org.apache.ibatis.thread;import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.ibatis.session.Configuration;
import org.apache.log4j.Logger;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;/**
* 刷新使用进程
*
* @author gongtao
* @date 2019-04-09
*/
public class RefreshRunnable implements java.lang.Runnable { public static Logger log = Logger.getLogger(RefreshRunnable.class); private Resource[] mapperLocations;
private Configuration configuration; /**
* 上一次刷新时间
*/
private Long beforeTime = 0L;
/**
* 是否执行刷新
*/
private static boolean refresh = false;
/**
* 延迟刷新秒数
*/
private static int delaySeconds;
/**
* 休眠时间
*/
private static int sleepSeconds;
/**
* 是否启用
*/
private static boolean enabled; /**
* 是否关闭
*/
private static boolean closed = false; /** 线程工厂 */
private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setNameFormat("mybatis-xml-reload-%d").build();
/** 定时扫描线程池 */
private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(1, THREAD_FACTORY); static {
delaySeconds = PropertiesUtil.getInt("delaySeconds");
sleepSeconds = PropertiesUtil.getInt("sleepSeconds");
enabled = "true".equals(PropertiesUtil.getString("enabled")); delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;
sleepSeconds = sleepSeconds == 0 ? 1 : sleepSeconds; log.debug("[delaySeconds] " + delaySeconds);
log.debug("[sleepSeconds] " + sleepSeconds); } public static boolean isRefresh() {
return refresh;
} public RefreshRunnable(Resource[] mapperLocations, Configuration configuration) {
this.mapperLocations = mapperLocations;
this.configuration = configuration;
} @Override
public void run() { beforeTime = System.currentTimeMillis();
if (enabled) {
start(this);
}
} public void start(final RefreshRunnable runnable) {
refresh = true;
System.out.println("Enable refresh mybatis xml thread...");
EXECUTOR.scheduleAtFixedRate(() -> {
try {
runnable.refresh(mapperLocations, beforeTime);
} catch (Exception e) {
e.printStackTrace();
}
}, delaySeconds, sleepSeconds, TimeUnit.SECONDS);
} /**
* 关闭热加载线程
*/
public static void closeReloadThread(){
log.info("Shutting down mybatis-xml-reload scheduled job");
if (enabled && refresh && !closed){
EXECUTOR.shutdown();
closed = true;
}
} /**
* 执行刷新
*
* @param mapperLocations 刷新目录
* @param beforeTime 上次刷新时间
* @throws NestedIOException 解析异常
* @throws FileNotFoundException 文件未找到
*/
public void refresh(Resource[] mapperLocations, Long beforeTime) throws Exception { // 本次刷新时间
Long refreshTime = System.currentTimeMillis(); List<File> refreshList = this.getRefreshFile(mapperLocations,
beforeTime);
if (refreshList.size() > 0) {
log.debug("refresh files:" + refreshList.size());
}
for (File file : refreshList) {
System.out.println("Refresh file: " + file.getAbsolutePath());
log.debug("refresh file:" + file.getAbsolutePath());
log.debug("refresh filename:" + file.getName()); // 清理已加载的资源标识,方便让它重新加载。
Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");
loadedResourcesField.setAccessible(true);
Set loadedResourcesSet = ((Set) loadedResourcesField.get(configuration));
loadedResourcesSet.remove(file.getAbsolutePath()); SqlSessionFactoryBean.refresh(new FileInputStream(file),
file.getAbsolutePath(), configuration);
}
// 如果刷新了文件,则修改刷新时间,否则不修改
if (refreshList.size() > 0) {
this.beforeTime = refreshTime;
}
} /**
* 获取需要刷新的文件列表
*
* @param mapperLocations 目录
* @param beforeTime 上次刷新时间
* @return 刷新文件列表
*/
private List<File> getRefreshFile(Resource[] mapperLocations, Long beforeTime) {
List<File> refreshList = new ArrayList<>();
for (Resource mapperLocation : mapperLocations) {
try {
if (mapperLocation instanceof FileSystemResource) {
File file = mapperLocation.getFile();
if (this.check(file, beforeTime)) {
refreshList.add(file);
}
}
} catch (IOException e) {
log.error("get file error", e);
}
} return refreshList;
} /**
* 判断文件是否需要刷新
*
* @param file 文件
* @param beforeTime 上次刷新时间
* @return 需要刷新返回true,否则返回false
*/
public boolean check(File file, Long beforeTime) {
return file.lastModified() > beforeTime;
}}

增加刷新xml的配置文件及工具类:

package org.apache.ibatis.thread;import java.util.Objects;
import java.util.Properties;public class PropertiesUtil { private static String filename = "/mybatis-refresh.properties";
private static Properties pro = new Properties();
static {
try {
pro.load(PropertiesUtil.class.getResourceAsStream(filename));
} catch (Exception e) {
e.printStackTrace();
System.out.println("Load mybatis-refresh “"+filename+"” file error.");
}
} public static int getInt(String key) {
int i = 0;
try {
i = Integer.parseInt(Objects.requireNonNull(getString(key)));
} catch (Exception e) {
//ignore
}
return i;
} public static String getString(String key) {
return pro == null ? null : pro.getProperty(key);
}}

配置文件mybatis-refresh.xml:

# enabled
enabled=true
# project start time[项目启动时间]
delaySeconds=5
# xml scanning interval time[扫描间隔时长]
sleepSeconds=2

这样就OK了,但是还有个问题,就是在关闭spring容器时,由于刷新线程一直存在,导致不能完全优雅关闭容器,故增加关闭刷新线程操作:

package com.xx.common.listener;import org.apache.ibatis.thread.RefreshRunnable;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;/**
* spring 关闭时监听器
* @author gongtao
* @date 2019-04-11 9:37
**/
@Component
public class ContainerClosedListener implements ApplicationListener<ContextClosedEvent> { @Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
//关闭mybatis 热加载线程
RefreshRunnable.closeReloadThread();
}
}

如此,大功告成!


    					
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,958
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,482
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,328
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,111
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,743
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,777