import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.log4j.Logger;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import com.google.common.collect.Sets;
/**
-
刷新MyBatis Mapper XML 線程
-
@author ThinkGem
-
@version 2016-5-29
*/
public class MapperRefresh implements java.lang.Runnable {public static Logger log = Logger.getLogger(MapperRefresh.class);
private static String filename = “/mybatis-refresh.properties”;
private static Properties prop = new Properties();private static boolean enabled; // 是否啓用Mapper刷新線程功能
private static boolean refresh; // 刷新啓用後,是否啓動了刷新線程private Set<String> location; // Mapper實際資源路徑
private Resource[] mapperLocations; // Mapper資源路徑
private Configuration configuration; // MyBatis配置對象private Long beforeTime = 0L; // 上一次刷新時間
private static int delaySeconds; // 延遲刷新秒數
private static int sleepSeconds; // 休眠時間
private static String mappingPath; // xml文件夾匹配字符串,需要根據需要修改static {
try { prop.load(MapperRefresh.class.getResourceAsStream(filename)); } catch (Exception e) { e.printStackTrace(); System.out.println("Load mybatis-refresh “"+filename+"” file error."); } enabled = "true".equalsIgnoreCase(getPropString("enabled")); delaySeconds = getPropInt("delaySeconds"); sleepSeconds = getPropInt("sleepSeconds"); mappingPath = getPropString("mappingPath"); delaySeconds = delaySeconds == 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<File> 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<File> getRefreshFile(File dir, Long beforeTime) {
List<File> fileList = new ArrayList<File>();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<V> extends HashMap<String, V> {private static final long serialVersionUID = -4950446264854982944L;
private String name;public StrictMap(String name, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this.name = name;
}public StrictMap(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}public StrictMap(String name) {
super();
this.name = name;
}public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}@SuppressWarnings(“unchecked”)
public V put(String key, V value) {
// ThinkGem 如果現在狀態爲刷新,則刷新(先刪除後添加)
if (MapperRefresh.isRefresh()) {
remove(key);
MapperRefresh.log.debug(“refresh key:” + key.substring(key.lastIndexOf(".") + 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<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
- private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
- private Resource configLocation;
- private Resource[] mapperLocations;
- private DataSource dataSource;
- private TransactionFactory transactionFactory;
- private Properties configurationProperties;
- private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
- private SqlSessionFactory sqlSessionFactory;
- //EnvironmentAware requires spring 3.1
- private String environment = SqlSessionFactoryBean.class.getSimpleName();
- private boolean failFast;
- private Interceptor[] plugins;
- private TypeHandler<?>[] typeHandlers;
- private String typeHandlersPackage;
- private Class<?>[] typeAliases;
- private String typeAliasesPackage;
- private Class<?> typeAliasesSuperType;
- //issue #19. No default provider.
- private DatabaseIdProvider databaseIdProvider;
- private ObjectFactory objectFactory;
- private ObjectWrapperFactory objectWrapperFactory;
- /**
- * Sets the ObjectFactory.
- *
- * @since 1.1.2
- * @param objectFactory
- */
- public void setObjectFactory(ObjectFactory objectFactory) {
- this.objectFactory = objectFactory;
- }
- /**
- * Sets the ObjectWrapperFactory.
- *
- * @since 1.1.2
- * @param objectWrapperFactory
- */
- public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
- this.objectWrapperFactory = objectWrapperFactory;
- }
- /**
- * Gets the DatabaseIdProvider
- *
- * @since 1.1.0
- * @return
- */
- public DatabaseIdProvider getDatabaseIdProvider() {
- return databaseIdProvider;
- }
- /**
- * Sets the DatabaseIdProvider.
- * As of version 1.2.2 this variable is not initialized by default.
- *
- * @since 1.1.0
- * @param databaseIdProvider
- */
- public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
- this.databaseIdProvider = databaseIdProvider;
- }
- /**
- * Mybatis plugin list.
- *
- * @since 1.0.1
- *
- * @param plugins list of plugins
- *
- */
- public void setPlugins(Interceptor[] plugins) {
- this.plugins = plugins;
- }
- /**
- * Packages to search for type aliases.
- *
- * @since 1.0.1
- *
- * @param typeAliasesPackage package to scan for domain objects
- *
- */
- public void setTypeAliasesPackage(String typeAliasesPackage) {
- this.typeAliasesPackage = typeAliasesPackage;
- }
- /**
- * Super class which domain objects have to extend to have a type alias created.
- * No effect if there is no package to scan configured.
- *
- * @since 1.1.2
- *
- * @param typeAliasesSuperType super class for domain objects
- *
- */
- public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
- this.typeAliasesSuperType = typeAliasesSuperType;
- }
- /**
- * Packages to search for type handlers.
- *
- * @since 1.0.1
- *
- * @param typeHandlersPackage package to scan for type handlers
- *
- */
- public void setTypeHandlersPackage(String typeHandlersPackage) {
- this.typeHandlersPackage = typeHandlersPackage;
- }
- /**
- * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
- *
- * @since 1.0.1
- *
- * @param typeHandlers Type handler list
- */
- public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
- this.typeHandlers = typeHandlers;
- }
- /**
- * List of type aliases to register. They can be annotated with {@code Alias}
- *
- * @since 1.0.1
- *
- * @param typeAliases Type aliases list
- */
- public void setTypeAliases(Class<?>[] typeAliases) {
- this.typeAliases = typeAliases;
- }
- /**
- * If true, a final check is done on Configuration to assure that all mapped
- * statements are fully loaded and there is no one still pending to resolve
- * includes. Defaults to false.
- *
- * @since 1.0.1
- *
- * @param failFast enable failFast
- */
- public void setFailFast(boolean failFast) {
- this.failFast = failFast;
- }
- /**
- * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
- * "WEB-INF/mybatis-configuration.xml".
- */
- public void setConfigLocation(Resource configLocation) {
- this.configLocation = configLocation;
- }
- /**
- * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}
- * configuration at runtime.
- *
- * This is an alternative to specifying "<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.
- *
- * <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any
- * attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if
- * a transaction is active.
- *
- * @see SpringManagedTransactionFactory
- * @param transactionFactory the MyBatis TransactionFactory
- */
- public void setTransactionFactory(TransactionFactory transactionFactory) {
- this.transactionFactory = transactionFactory;
- }
- /**
- * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis
- * config file. This is used only as a placeholder name. The default value is
- * {@code SqlSessionFactoryBean.class.getSimpleName()}.
- *
- * @param environment the environment name
- */
- public void setEnvironment(String environment) {
- this.environment = environment;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public void afterPropertiesSet() throws Exception {
- notNull(dataSource, "Property 'dataSource' is required");
- notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
- this.sqlSessionFactory = buildSqlSessionFactory();
- }
- /**
- * Build a {@code SqlSessionFactory} instance.
- *
- * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
- * {@code SqlSessionFactory} instance based on an Reader.
- *
- * @return SqlSessionFactory
- * @throws IOException if loading the config file failed
- */
- protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
- Configuration configuration;
- XMLConfigBuilder xmlConfigBuilder = null;
- if (this.configLocation != null) {
- xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
- configuration = xmlConfigBuilder.getConfiguration();
- } else {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
- }
- configuration = new Configuration();
- configuration.setVariables(this.configurationProperties);
- }
- if (this.objectFactory != null) {
- configuration.setObjectFactory(this.objectFactory);
- }
- if (this.objectWrapperFactory != null) {
- configuration.setObjectWrapperFactory(this.objectWrapperFactory);
- }
- if (hasLength(this.typeAliasesPackage)) {
- String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
- ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
- for (String packageToScan : typeAliasPackageArray) {
- // ThinkGem 修改實體類重名的時候拋出並打印異常,否則系統會一直遞歸造成無法啓動
- try {
- configuration.getTypeAliasRegistry().registerAliases(packageToScan,
- typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
- } catch (Exception ex) {
- LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);
- throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);
- } finally {
- ErrorContext.instance().reset();
- }
- // ThinkGem end
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
- }
- }
- }
- if (!isEmpty(this.typeAliases)) {
- for (Class<?> typeAlias : this.typeAliases) {
- configuration.getTypeAliasRegistry().registerAlias(typeAlias);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Registered type alias: '" + typeAlias + "'");
- }
- }
- }
- if (!isEmpty(this.plugins)) {
- for (Interceptor plugin : this.plugins) {
- configuration.addInterceptor(plugin);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Registered plugin: '" + plugin + "'");
- }
- }
- }
- if (hasLength(this.typeHandlersPackage)) {
- String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
- ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
- for (String packageToScan : typeHandlersPackageArray) {
- configuration.getTypeHandlerRegistry().register(packageToScan);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
- }
- }
- }
- if (!isEmpty(this.typeHandlers)) {
- for (TypeHandler<?> typeHandler : this.typeHandlers) {
- configuration.getTypeHandlerRegistry().register(typeHandler);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Registered type handler: '" + typeHandler + "'");
- }
- }
- }
- if (xmlConfigBuilder != null) {
- try {
- xmlConfigBuilder.parse();
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
- }
- } catch (Exception ex) {
- throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
- } finally {
- ErrorContext.instance().reset();
- }
- }
- if (this.transactionFactory == null) {
- this.transactionFactory = new SpringManagedTransactionFactory();
- }
- configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
- if (this.databaseIdProvider != null) {
- try {
- configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
- } catch (SQLException e) {
- throw new NestedIOException("Failed getting a databaseId", e);
- }
- }
- if (!isEmpty(this.mapperLocations)) {
- for (Resource mapperLocation : this.mapperLocations) {
- if (mapperLocation == null) {
- continue;
- }
- try {
- XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
- configuration, mapperLocation.toString(), configuration.getSqlFragments());
- xmlMapperBuilder.parse();
- } catch (Exception e) {
- // ThinkGem MapperXML有錯誤的時候拋出並打印異常,否則系統會一直遞歸造成無法啓動
- LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);
- throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
- } finally {
- ErrorContext.instance().reset();
- }
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
- }
- }
- // ThinkGem 啓動刷新MapperXML定時器(有助於開發者調試)。
- new MapperRefresh(this.mapperLocations, configuration).run();
- } else {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
- }
- }
- return this.sqlSessionFactoryBuilder.build(configuration);
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public SqlSessionFactory getObject() throws Exception {
- if (this.sqlSessionFactory == null) {
- afterPropertiesSet();
- }
- return this.sqlSessionFactory;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public Class<? extends SqlSessionFactory> getObjectType() {
- return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isSingleton() {
- return true;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public void onApplicationEvent(ApplicationEvent event) {
- if (failFast && event instanceof ContextRefreshedEvent) {
- // fail-fast -> check all statements are completed
- this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
- }
- }
- }
/**
* 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
- @modify ThinkGem 2016-5-24 來自 MyBatisSpring1.2.3版本
*/
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
private Resource configLocation;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
//EnvironmentAware requires spring 3.1
private String environment = SqlSessionFactoryBean.class.getSimpleName();
private boolean failFast;
private Interceptor[] plugins;
private TypeHandler<?>[] typeHandlers;
private String typeHandlersPackage;
private Class<?>[] typeAliases;
private String typeAliasesPackage;
private Class<?> typeAliasesSuperType;
//issue #19. No default provider.
private DatabaseIdProvider databaseIdProvider;
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory;
/**
- Sets the ObjectFactory.
- @since 1.1.2
- @param objectFactory
*/
public void setObjectFactory(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
/**
- Sets the ObjectWrapperFactory.
- @since 1.1.2
- @param objectWrapperFactory
*/
public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
this.objectWrapperFactory = objectWrapperFactory;
}
/**
- Gets the DatabaseIdProvider
- @since 1.1.0
- @return
*/
public DatabaseIdProvider getDatabaseIdProvider() {
return databaseIdProvider;
}
/**
- Sets the DatabaseIdProvider.
- As of version 1.2.2 this variable is not initialized by default.
- @since 1.1.0
- @param databaseIdProvider
*/
public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
this.databaseIdProvider = databaseIdProvider;
}
/**
- Mybatis plugin list.
- @since 1.0.1
- @param plugins list of plugins
*/
public void setPlugins(Interceptor[] plugins) {
this.plugins = plugins;
}
/**
- Packages to search for type aliases.
- @since 1.0.1
- @param typeAliasesPackage package to scan for domain objects
*/
public void setTypeAliasesPackage(String typeAliasesPackage) {
this.typeAliasesPackage = typeAliasesPackage;
}
/**
- Super class which domain objects have to extend to have a type alias created.
- No effect if there is no package to scan configured.
- @since 1.1.2
- @param typeAliasesSuperType super class for domain objects
*/
public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
this.typeAliasesSuperType = typeAliasesSuperType;
}
/**
- Packages to search for type handlers.
- @since 1.0.1
- @param typeHandlersPackage package to scan for type handlers
*/
public void setTypeHandlersPackage(String typeHandlersPackage) {
this.typeHandlersPackage = typeHandlersPackage;
}
/**
- Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}
- @since 1.0.1
- @param typeHandlers Type handler list
*/
public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
this.typeHandlers = typeHandlers;
}
/**
- List of type aliases to register. They can be annotated with {@code Alias}
- @since 1.0.1
- @param typeAliases Type aliases list
*/
public void setTypeAliases(Class<?>[] typeAliases) {
this.typeAliases = typeAliases;
}
/**
- If true, a final check is done on Configuration to assure that all mapped
- statements are fully loaded and there is no one still pending to resolve
- includes. Defaults to false.
- @since 1.0.1
- @param failFast enable failFast
*/
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
/**
- Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is
- “WEB-INF/mybatis-configuration.xml”.
*/
public void setConfigLocation(Resource configLocation) {
this.configLocation = configLocation;
}
/**
- Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory}
- configuration at runtime.
- This is an alternative to specifying “<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.
- <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any
- attempt at getting an SqlSession through Spring’s MyBatis framework will throw an exception if
- a transaction is active.
- @see SpringManagedTransactionFactory
- @param transactionFactory the MyBatis TransactionFactory
*/
public void setTransactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
}
/**
- <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis
- config file. This is used only as a placeholder name. The default value is
- {@code SqlSessionFactoryBean.class.getSimpleName()}.
- @param environment the environment name
*/
public void setEnvironment(String environment) {
this.environment = environment;
}
/**
- {@inheritDoc}
*/
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, “Property ‘dataSource’ is required”);
notNull(sqlSessionFactoryBuilder, “Property ‘sqlSessionFactoryBuilder’ is required”);
this.sqlSessionFactory = buildSqlSessionFactory();
}
/**
- Build a {@code SqlSessionFactory} instance.
- The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
- {@code SqlSessionFactory} instance based on an Reader.
- @return SqlSessionFactory
- @throws IOException if loading the config file failed
*/
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
// ThinkGem 修改實體類重名的時候拋出並打印異常,否則系統會一直遞歸造成無法啓動
try {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
} catch (Exception ex) {
LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);
throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);
} finally {
ErrorContext.instance().reset();
}
// ThinkGem end
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
// ThinkGem MapperXML有錯誤的時候拋出並打印異常,否則系統會一直遞歸造成無法啓動
LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
// ThinkGem 啓動刷新MapperXML定時器(有助於開發者調試)。
new MapperRefresh(this.mapperLocations, configuration).run();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
/**
- {@inheritDoc}
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
/**
- {@inheritDoc}
*/
@Override
public Class<? extends SqlSessionFactory> getObjectType() {
return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
}
/**
- {@inheritDoc}
*/
@Override
public boolean isSingleton() {
return true;
}
/**
- {@inheritDoc}
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
}
重寫SqlSessionFactoryBean就的修改下Spring的MyBatis配置部分:
- <!-- MyBatis SqlSessionFactoryBean -->
- <bean id="sqlSessionFactory" class="com.thinkgem.jeesite.common.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource"/>
- <property name="typeAliasesPackage" value="com.thinkgem.jeesite"/>
- <property name="typeAliasesSuperType" value="<span style="line-height: 1.5;">com.thinkgem.jeesite</span><span style="line-height: 1.5;">.persistence.BaseEntity"/></span>
- <property name="mapperLocations" value="classpath*:/mappings/**/*.xml"/>
- <property name="configLocation" value="classpath:/mybatis-config.xml"></property>
- </bean>
<!-- MyBatis SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="com.thinkgem.jeesite.common.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.thinkgem.jeesite"/> <property name="typeAliasesSuperType" value="com.thinkgem.jeesite.persistence.BaseEntity"/> <property name="mapperLocations" value="classpath*:/mappings/**/*.xml"/> <property name="configLocation" value="classpath:/mybatis-config.xml"></property> </bean>
最後附加上屬性配置文件:mybatis-refresh.properties
- #是否開啓刷新線程
- enabled=true
- #延遲啓動刷新程序的秒數
- delaySeconds=60
- #刷新掃描間隔的時長秒數
- sleepSeconds=3
- #掃描Mapper文件的資源路徑
- mappingPath=mappings
#是否開啓刷新線程
enabled=true
#延遲啓動刷新程序的秒數
delaySeconds=60
#刷新掃描間隔的時長秒數
sleepSeconds=3
#掃描Mapper文件的資源路徑
mappingPath=mappings
<div id="share_weibo">分享到:
<a data-type="sina" href="javascript:;" title="分享到新浪微博"><img src="/images/sina.jpg"></a>
<a data-type="qq" href="javascript:;" title="分享到騰訊微博"><img src="/images/tec.jpg"></a>
</div>
- 2016-06-13 10:40
- 瀏覽 26246
- 評論(9)
<li>分類:<a href="https://www.iteye.com/blogs/category/architecture">企業架構</a></li>
<li class="last"><a href="https://www.iteye.com/wiki/blog/2304557" target="_blank" class="more">查看更多</a></li>
</ul>
2018-06-07 15:42:44 [com.cjis.util.MapperRefresh]-[DEBUG] [delaySeconds] 60
2018-06-07 15:42:44 [com.cjis.util.MapperRefresh]-[DEBUG] [sleepSeconds] 3
2018-06-07 15:42:44 [com.cjis.util.MapperRefresh]-[DEBUG] [mappingPath] com/cjis/mapping
2018-06-07 15:42:51 [com.cjis.util.MapperRefresh]-[DEBUG] [location] null
2018-06-07 15:42:52 [com.cjis.util.MapperRefresh]-[DEBUG] [configuration] org.apache.ibatis.session.Configuration@9367551
Exception in thread "MyBatis-Mapper-Refresh" java.lang.NoClassDefFoundError: com/google/common/collect/Sets
at com.cjis.util.MapperRefresh$1.run(MapperRefresh.java:101)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Sets
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
... 2 more
按照樓主的操作,報錯呢 請問這是爲啥
文件第5行一定要這樣寫麼?
發表評論
相關資源推薦
<li class="news-recommends-ajax">
<a href="https://download.csdn.net/download/qq_38686665/10202917" data-track-click="{"con":",https://download.csdn.net/download/qq_38686665/10202917,-","mod":"popu_712"}" target="_blank" title="mybatis熱部署mapper增量更新."><em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱部署</em><em class="related_suggestion_highlight">mapper</em><em class="related_suggestion_highlight">增量</em>更新.</a>
<p>
通常項目中如果修改<em class="related_suggestion_highlight">mapper</em>.<em class="related_suggestion_highlight">xml文件</em> 就要重啓服務器才生效.本資源直接換包即用.<em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">熱部署</em>
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/baochanghong/article/details/51939115" data-track-click="{"con":",https://blog.csdn.net/baochanghong/article/details/51939115,-","mod":"popu_712"}" target="_blank" title="實現MyBatis Mapper XML文件增量動態刷新,自動加載,熱加載,熱部署"><em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">MyBatis</em> <em class="related_suggestion_highlight">Mapper</em> <em class="related_suggestion_highlight">XML文件</em><em class="related_suggestion_highlight">增量</em><em class="related_suggestion_highlight">動態刷新</em>,<em class="related_suggestion_highlight">自動加載</em>,<em class="related_suggestion_highlight">熱加載</em>,<em class="related_suggestion_highlight">熱部署</em></a>
<p>
最初啓動服務後<em class="related_suggestion_highlight">Mapper</em> <em class="related_suggestion_highlight">XML文件</em>,必須重啓服務才能生效,這樣就大大影響了我們的開發效率。
網上同學們也有實現類似功能,但都是全部清空,全部刷新XML,這樣硬件消耗比較嚴重,加載時間也比較長。我們只修改了幾行SQL就沒有必要全部加載,只需要加載修改的問題就行了。
後來爲了急需解決這個問題,進行修改MyBatis源碼實現Mapper XML增量刷新,直接覆蓋方式實現,
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/LOVELONG8808/article/details/78738086" data-track-click="{"con":",https://blog.csdn.net/LOVELONG8808/article/details/78738086,-","mod":"popu_712"}" target="_blank" title="mybatis熱部署加載*Mapper.xml文件,手動刷新*Mapper.xml文件"><em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱部署</em>加載*<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em>,手動刷新*<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em></a>
<p>
由於項目已經發布到線上,要是修改一個<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em>的話,需要重啓整個服務,這個是很耗時間的,而且在一段時間內導致服務不可用,嚴重影響用戶
的體驗度。所以希望可以有一個機制可以,當修改某個mapper.xml的時候,只要重新加載這個mapper.xml就好了,參考網上的一些資料和demo,加上一些
自己的總結,下面的代碼是通過測試的,可以供你們參考和使用。
import java.i
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/CS568591377/article/details/37935571" data-track-click="{"con":",https://blog.csdn.net/CS568591377/article/details/37935571,-","mod":"popu_712"}" target="_blank" title="mybatis熱部署加載*Mapper.xml文件"><em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱部署</em>加載*<em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em></a>
<p>
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/sinat_35626559/article/details/80988053" data-track-click="{"con":",https://blog.csdn.net/sinat_35626559/article/details/80988053,-","mod":"popu_712"}" target="_blank" title="Spring學習——手動實現Mapper.xml文件的熱部署">Spring學習——手動<em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">Mapper</em>.<em class="related_suggestion_highlight">xml文件</em>的<em class="related_suggestion_highlight">熱部署</em></a>
<p>
原文轉載自:https://blog.csdn.net/lovelong8808/article/details/78738086
轉載使用後,根據實際情況有做部分修改,感謝原文博主提供的刷新方法。
注意,目前該方法只支持全量刷新,即刷新指定路徑下的所有Mapper.xml文件,不支持僅刷新修改後的部分Mapper.xml。
由於項目已經發布到線上,要是修改一個Mapper.xm…
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/qq_38686665/article/details/79046328" data-track-click="{"con":",https://blog.csdn.net/qq_38686665/article/details/79046328,-","mod":"popu_712"}" target="_blank" title="超簡單的mybatis的mapper文件增量熱部署">超簡單的<em class="related_suggestion_highlight">mybatis</em>的<em class="related_suggestion_highlight">mapper</em>文件<em class="related_suggestion_highlight">增量</em><em class="related_suggestion_highlight">熱部署</em></a>
<p>
通常項目中如果修改<em class="related_suggestion_highlight">mapper</em>.<em class="related_suggestion_highlight">xml文件</em> 就要重啓服務器才生效。網上雖然都有類似的教程,但是很多都是根本用不了.或者每次都是全量更新.本文基於http://blog.csdn.net/chunge48596/article/details/53539126?locationNum=1&fps=1的方法上更進一步優化,直接<em class="related_suggestion_highlight">實現</em>換包即用.
原文中對SqlSessionFactoryBean.cla
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/u014780287/article/details/78800697" data-track-click="{"con":",https://blog.csdn.net/u014780287/article/details/78800697,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/u014780287/article/details/78800697,-","mod":"popu_712"}" target="_blank" title="Mybatis的xml修改後自動刷新(不改源碼)"><em class="related_suggestion_highlight">Mybatis</em>的xml修改後自動刷新(不改源碼)</a>
<p>
思路來自於網絡大神的啓發,後來發現需要改源碼對系統繼承不太方便。嘗試了一下發現<em class="related_suggestion_highlight">mybatis</em>已經給我們留下了足夠多的可擴展點。
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/iteye_14109/article/details/82400826" data-track-click="{"con":",https://blog.csdn.net/iteye_14109/article/details/82400826,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/iteye_14109/article/details/82400826,-","mod":"popu_712"}" target="_blank" title="jrebel下載及配置(tomcat熱部署)--修改java類文件、xml文件或properties資源文件自動重新加載...">jrebel下載及配置(tomcat<em class="related_suggestion_highlight">熱部署</em>)--修改java類文件、<em class="related_suggestion_highlight">xml文件</em>或properties資源文件自動重新加載...</a>
<p>
Jrebel 介紹:
Jrebel 可快速實現熱部署,節省了大量重啓時間,提高了個人開發效率
JRebel是一款JAVA虛擬機插件,它使得JAVA程序員能在不進行重部署的情況下,即時看到代碼的改變對一個應用程序帶來的影響。JRebel使你能即時分別看到代碼、類和資源的變化,你可以一個個地上傳而不是一次性全部部署。當程序員在開發環境中對任何一個類或者資源作出修改的時候,這個變化會直接反應在部署好…
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/chao_1990/article/details/85116284" data-track-click="{"con":",https://blog.csdn.net/chao_1990/article/details/85116284,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/chao_1990/article/details/85116284,-","mod":"popu_712"}" target="_blank" title="Mybatis實現*mapper.xml熱部署-分子級更新"><em class="related_suggestion_highlight">Mybatis</em><em class="related_suggestion_highlight">實現</em>*<em class="related_suggestion_highlight">mapper</em>.xml<em class="related_suggestion_highlight">熱部署</em>-分子級更新</a>
<p>
需求:
項目在開發階段或是修復bug階段,會有修改mybatis的mapper.xml的時候,修改一般情況都要重啓才能生失效,如果是分佈式項目重啓有時會耗時很久,都是無盡的等待。如果頻繁修改,那麼時間都浪費到等待重啓的過程。
目標:
實現mybatis的mapper.xml文件修改後熱部署,而且只熱更新修改了的xml,可以提高重新解析過程的效率。
要求:
儘量滿足開閉原則
實現:
…
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/a4475686/article/details/79378000" data-track-click="{"con":",https://blog.csdn.net/a4475686/article/details/79378000,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/a4475686/article/details/79378000,-","mod":"popu_712"}" target="_blank" title="針對IDEA使用JRebel熱部署修改mybatis映射文件sql語句熱部署失敗的解決方案">針對IDEA使用JRebel<em class="related_suggestion_highlight">熱部署</em>修改<em class="related_suggestion_highlight">mybatis</em>映射文件sql語句<em class="related_suggestion_highlight">熱部署</em>失敗的解決方案</a>
<p>
本人開發環境:IDEA(2017.3) JRebel(7.X) 使用過一段時間的IDEA後感覺非常好用,並且搭配JRebel後開發簡直非常舒服。但是使用的過程中有個很煩的問題,就是修改sql映射文件時<em class="related_suggestion_highlight">熱部署</em>好像沒有反應。 這個問題我最後在JRebel官網的論壇上找到了答案,大家可以參考一下。同時,也友情提示一下,也許百度或谷歌搜半天的問題,去官網的論壇裏搜一下關鍵字一下便找到問...
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/Java_Dmz/article/details/83381725" data-track-click="{"con":",https://blog.csdn.net/Java_Dmz/article/details/83381725,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/Java_Dmz/article/details/83381725,-","mod":"popu_712"}" target="_blank" title="Springboot整合mybatis、以及xml配置實例、熱部署、打包、跳轉、ssl、webapp">Springboot整合<em class="related_suggestion_highlight">mybatis</em>、以及xml配置實例、<em class="related_suggestion_highlight">熱部署</em>、打包、跳轉、ssl、webapp</a>
<p>
整合<em class="related_suggestion_highlight">mybatis</em>:
引入jar包,這個包是dao+server整合,內涵mybatis生成的xml,及mapper接口和bean對象
引入包後,其包的依賴也會下來,所依賴的jar
yml文件配置連接參數,數據源如果有引入jar則還可以配置數據源,mybatis配置mapper接口在哪裏,需要文件路徑配置/,在配置bean對象在哪裏
找到啓動類:
mappersc…
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/yaoayao123/article/details/86558908" data-track-click="{"con":",https://blog.csdn.net/yaoayao123/article/details/86558908,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/yaoayao123/article/details/86558908,-","mod":"popu_712"}" target="_blank" title="eclipse jrebel properties,xml等配置文件不能熱部署">eclipse jrebel properties,xml等配置文件不能<em class="related_suggestion_highlight">熱部署</em></a>
<p>
博主是裝的jrebel8.2.4最新版本,裝完後發現properties,xml等配置文件不能<em class="related_suggestion_highlight">實現</em><em class="related_suggestion_highlight">熱部署</em>,google了半天沒有找到解決辦法…至少到放棄的這一刻,還是沒有找到辦法,但是有個辦法可以一定程度上解決這個問題,如下圖操作:
這個辦法能夠將文件部署到webapp下,但是不能加載到內存中
…
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/Sopp_Li/article/details/79800169" data-track-click="{"con":",https://blog.csdn.net/Sopp_Li/article/details/79800169,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/Sopp_Li/article/details/79800169,-","mod":"popu_712"}" target="_blank" title="SpringBoot整合Mybatis,使用通用mapper插件的時候,熱部署報錯,如何解決?">SpringBoot整合<em class="related_suggestion_highlight">Mybatis</em>,使用通用<em class="related_suggestion_highlight">mapper</em>插件的時候,<em class="related_suggestion_highlight">熱部署</em>報錯,如何解決?</a>
<p>
SpringBoot整合<em class="related_suggestion_highlight">Mybatis</em>,通用<em class="related_suggestion_highlight">mapper</em>插件<em class="related_suggestion_highlight">熱部署</em>報錯。。。。。。在使用SpringBoot 整合<em class="related_suggestion_highlight">mybatis</em>的時候,爲了減少不必要的代碼開發量,我們會使用<em class="related_suggestion_highlight">mybatis</em>的通用<em class="related_suggestion_highlight">mapper</em>插件,tk.<em class="related_suggestion_highlight">mapper</em>,首先引入如下的依賴:&amp;lt;plugin&amp;gt; &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;...
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/love_dl_forever/article/details/79106800" data-track-click="{"con":",https://blog.csdn.net/love_dl_forever/article/details/79106800,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/love_dl_forever/article/details/79106800,-","mod":"popu_712"}" target="_blank" title="mac自動加載即熱部署">mac<em class="related_suggestion_highlight">自動加載</em>即<em class="related_suggestion_highlight">熱部署</em></a>
<p>
idea的<em class="related_suggestion_highlight">熱部署</em>
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://download.csdn.net/download/liuming690452074/11181158" data-track-click="{"con":",https://download.csdn.net/download/liuming690452074/11181158,-","mod":"popu_712"}" data-track-view="{"con":",https://download.csdn.net/download/liuming690452074/11181158,-","mod":"popu_712"}" target="_blank" title="IDEA熱部署修改mybatis映射文件工具 jr-ide-intellij-nightly.zip">IDEA<em class="related_suggestion_highlight">熱部署</em>修改<em class="related_suggestion_highlight">mybatis</em>映射文件工具 jr-ide-intellij-nightly.zip</a>
<p>
IDEA<em class="related_suggestion_highlight">熱部署</em>修改<em class="related_suggestion_highlight">mybatis</em>映射文件工具 jr-ide-intellij-nightly.zip
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://download.csdn.net/download/qinzhengtime/9940461" data-track-click="{"con":",https://download.csdn.net/download/qinzhengtime/9940461,-","mod":"popu_712"}" data-track-view="{"con":",https://download.csdn.net/download/qinzhengtime/9940461,-","mod":"popu_712"}" target="_blank" title="6.5熱部署修改xml不重啓">6.5<em class="related_suggestion_highlight">熱部署</em>修改xml不重啓</a>
<p>
6.5<em class="related_suggestion_highlight">熱部署</em>修改xml不重啓
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://download.csdn.net/download/mate_ge/10233712" data-track-click="{"con":",https://download.csdn.net/download/mate_ge/10233712,-","mod":"popu_712"}" data-track-view="{"con":",https://download.csdn.net/download/mate_ge/10233712,-","mod":"popu_712"}" target="_blank" title="spring boot中配置mybatis熱加載.zip">spring boot中配置<em class="related_suggestion_highlight">mybatis</em><em class="related_suggestion_highlight">熱加載</em>.zip</a>
<p>
spring boot中配置<em class="related_suggestion_highlight">mybatis</em> xml資源文件<em class="related_suggestion_highlight">熱加載</em>的方法以及相關文件
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/qq_32635069/article/details/83655625" data-track-click="{"con":",https://blog.csdn.net/qq_32635069/article/details/83655625,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/qq_32635069/article/details/83655625,-","mod":"popu_712"}" target="_blank" title="springBoot實現配置和實例的熱更新,集成Apollo,方法通用">springBoot<em class="related_suggestion_highlight">實現</em>配置和實例的熱更新,集成Apollo,方法通用</a>
<p>
</p>
</li>
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/xiaoxian8023/article/details/8715837" data-track-click="{"con":",https://blog.csdn.net/xiaoxian8023/article/details/8715837,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/xiaoxian8023/article/details/8715837,-","mod":"popu_712"}" target="_blank" title="web動態部署(熱部署)">web動態部署(<em class="related_suggestion_highlight">熱部署</em>)</a>
<p>
今天跟大家探討一下關於web動態部署,也就是<em class="related_suggestion_highlight">熱部署</em>的問題。說這個之前,先說一個敏捷開發的原則。
【最小發布、增量開發】
我們在做項目時,設定的期限都特別長。總是想第一個版本就想把所有想到的問題都做完,以至於項目一再延期。所以我們應該改變我們的開發策略。採用敏捷開發的方式。
這裏我想強調的有2點,1.最小發布。2.增量開發。
對於最小發布,就是要在第一版中把核心功能實現,即立
<li class="news-recommends-ajax">
<a href="https://blog.csdn.net/Cs_hnu_scw/article/details/78961232" data-track-click="{"con":",https://blog.csdn.net/Cs_hnu_scw/article/details/78961232,-","mod":"popu_712"}" data-track-view="{"con":",https://blog.csdn.net/Cs_hnu_scw/article/details/78961232,-","mod":"popu_712"}" target="_blank" title="手把手教你如何玩轉SpringBoot整合MyBatis(全註解開發和熱部署及其JSP配置詳解)">手把手教你如何玩轉SpringBoot整合<em class="related_suggestion_highlight">MyBatis</em>(全註解開發和<em class="related_suggestion_highlight">熱部署</em>及其JSP配置詳解)</a>
<p>
當寫了前一篇關於SpringBoot的文章之後,有很多朋友就提問說,關於SpringBoot整合Mybatis,還有SpringBoot熱部署,並且還有說關於整合JSP配置的一些問題,然後決定寫這一篇文章來幫有疑惑的朋友來解決一下問題。(SpringBoot整合Hibernate的使用,及其SpringBoot的基本知識可以參考之前的一篇章 SpringBoot基礎學習 )
…
</ul>
</div>
評論