zoukankan      html  css  js  c++  java
  • mybatis 热部署xml文件(spring boot和springmvc两种方式)

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

    步骤:1、创建两个java类

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

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

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

     第一步:MapperRefresh.java 

    /** 
     * Copyright (c) 2012-Now https://github.com/thinkgem/jeesite. 
     */  
    package com.thinkgem.jeesite.mybatis.thread;  
      
    import java.io.File;  
    import java.io.FileInputStream;  
    import java.io.FileNotFoundException;  
    import java.io.InputStream;  
    import java.lang.reflect.Field;  
    import java.util.ArrayList;  
    import java.util.HashMap;  
    import java.util.List;  
    import java.util.Map;  
    import java.util.Properties;  
    import java.util.Set;  
      
    import org.apache.commons.lang3.StringUtils;  
    import org.apache.ibatis.builder.xml.XMLMapperBuilder;  
    import org.apache.ibatis.executor.ErrorContext;  
    import org.apache.ibatis.session.Configuration;  
    import org.apache.log4j.Logger;  
    import org.springframework.core.NestedIOException;  
    import org.springframework.core.io.Resource;  
      
    import com.google.common.collect.Sets;  
      
    /** 
     * 刷新MyBatis Mapper XML 线程 
     * @author ThinkGem 
     * @version 2016-5-29 
     */  
    public class MapperRefresh implements java.lang.Runnable {  
      
        public static Logger log = Logger.getLogger(MapperRefresh.class);  
      
        private static String filename = "/mybatis-refresh.properties";  
        private static Properties prop = new Properties();  
      
        private static boolean enabled;         // 是否启用Mapper刷新线程功能  
        private static boolean refresh;         // 刷新启用后,是否启动了刷新线程  
          
        private Set<String> location;         // Mapper实际资源路径  
          
        private Resource[] mapperLocations;     // Mapper资源路径  
        private Configuration configuration;        // MyBatis配置对象  
          
        private Long beforeTime = 0L;           // 上一次刷新时间  
        private static int delaySeconds;        // 延迟刷新秒数  
        private static int sleepSeconds;        // 休眠时间  
        private static String mappingPath;      // xml文件夹匹配字符串,需要根据需要修改  
      
        static {  
              
            try {  
                prop.load(MapperRefresh.class.getResourceAsStream(filename));  
            } catch (Exception e) {  
                e.printStackTrace();  
                System.out.println("Load mybatis-refresh “"+filename+"” file error.");  
            }  
      
            enabled = "true".equalsIgnoreCase(getPropString("enabled"));  
              
            delaySeconds = getPropInt("delaySeconds");  
            sleepSeconds = getPropInt("sleepSeconds");  
            mappingPath = getPropString("mappingPath");  
      
            delaySeconds = delaySeconds == 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;  
                }  
            }  
        }  
    }  

    第二步:SqlSessionFactoryBean.java   

    /** 
     *    Copyright 2010-2015 the original author or authors. 
     * 
     *    Licensed under the Apache License, Version 2.0 (the "License"); 
     *    you may not use this file except in compliance with the License. 
     *    You may obtain a copy of the License at 
     * 
     *       http://www.apache.org/licenses/LICENSE-2.0 
     * 
     *    Unless required by applicable law or agreed to in writing, software 
     *    distributed under the License is distributed on an "AS IS" BASIS, 
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
     *    See the License for the specific language governing permissions and 
     *    limitations under the License. 
     */  
    package com.thinkgem.jeesite.mybatis.spring;  
      
    import static org.springframework.util.Assert.notNull;  
    import static org.springframework.util.ObjectUtils.isEmpty;  
    import static org.springframework.util.StringUtils.hasLength;  
    import static org.springframework.util.StringUtils.tokenizeToStringArray;  
      
    import java.io.IOException;  
    import java.sql.SQLException;  
    import java.util.Properties;  
      
    import javax.sql.DataSource;  
      
    import org.apache.ibatis.builder.xml.XMLConfigBuilder;  
    import org.apache.ibatis.builder.xml.XMLMapperBuilder;  
    import org.apache.ibatis.executor.ErrorContext;  
    import org.apache.ibatis.logging.Log;  
    import org.apache.ibatis.logging.LogFactory;  
    import org.apache.ibatis.mapping.DatabaseIdProvider;  
    import org.apache.ibatis.mapping.Environment;  
    import org.apache.ibatis.plugin.Interceptor;  
    import org.apache.ibatis.reflection.factory.ObjectFactory;  
    import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;  
    import org.apache.ibatis.session.Configuration;  
    import org.apache.ibatis.session.SqlSessionFactory;  
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;  
    import org.apache.ibatis.transaction.TransactionFactory;  
    import org.apache.ibatis.type.TypeHandler;  
    import org.mybatis.spring.transaction.SpringManagedTransactionFactory;  
    import org.springframework.beans.factory.FactoryBean;  
    import org.springframework.beans.factory.InitializingBean;  
    import org.springframework.context.ApplicationEvent;  
    import org.springframework.context.ApplicationListener;  
    import org.springframework.context.ConfigurableApplicationContext;  
    import org.springframework.context.event.ContextRefreshedEvent;  
    import org.springframework.core.NestedIOException;  
    import org.springframework.core.io.Resource;  
    import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;  
      
    import com.thinkgem.jeesite.common.mybatis.thread.MapperRefresh;  
      
    /** 
     * {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}. 
     * This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context; 
     * the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection. 
     * 
     * Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction 
     * demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for transactions 
     * which span multiple databases or when container managed transactions (CMT) are being used. 
     * 
     * @author Putthibong Boonbong 
     * @author Hunter Presnall 
     * @author Eduardo Macarron 
     *  
     * @see #setConfigLocation 
     * @see #setDataSource 
     * @version $Id$ 
     * @modify ThinkGem 2016-5-24 来自 MyBatisSpring1.2.3版本 
     */  
    public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {  
      
      private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);  
      
      private Resource configLocation;  
      
      private Resource[] mapperLocations;  
      
      private DataSource dataSource;  
      
      private TransactionFactory transactionFactory;  
      
      private Properties configurationProperties;  
      
      private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();  
      
      private SqlSessionFactory sqlSessionFactory;  
      
      //EnvironmentAware requires spring 3.1  
      private String environment = SqlSessionFactoryBean.class.getSimpleName();  
      
      private boolean failFast;  
      
      private Interceptor[] plugins;  
      
      private TypeHandler<?>[] typeHandlers;  
      
      private String typeHandlersPackage;  
      
      private Class<?>[] typeAliases;  
      
      private String typeAliasesPackage;  
      
      private Class<?> typeAliasesSuperType;  
      
      //issue #19. No default provider.  
      private DatabaseIdProvider databaseIdProvider;  
      
      private ObjectFactory objectFactory;  
      
      private ObjectWrapperFactory objectWrapperFactory;  
      
      /** 
       * Sets the ObjectFactory. 
       *  
       * @since 1.1.2 
       * @param objectFactory 
       */  
      public void setObjectFactory(ObjectFactory objectFactory) {  
        this.objectFactory = objectFactory;  
      }  
      
      /** 
       * Sets the ObjectWrapperFactory. 
       *  
       * @since 1.1.2 
       * @param objectWrapperFactory 
       */  
      public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {  
        this.objectWrapperFactory = objectWrapperFactory;  
      }  
      
      /** 
       * Gets the DatabaseIdProvider 
       * 
       * @since 1.1.0 
       * @return 
       */  
      public DatabaseIdProvider getDatabaseIdProvider() {  
        return databaseIdProvider;  
      }  
      
      /** 
       * Sets the DatabaseIdProvider. 
       * As of version 1.2.2 this variable is not initialized by default.  
       * 
       * @since 1.1.0 
       * @param databaseIdProvider 
       */  
      public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {  
        this.databaseIdProvider = databaseIdProvider;  
      }  
      
      /** 
       * Mybatis plugin list. 
       * 
       * @since 1.0.1 
       * 
       * @param plugins list of plugins 
       * 
       */  
      public void setPlugins(Interceptor[] plugins) {  
        this.plugins = plugins;  
      }  
      
      /** 
       * Packages to search for type aliases. 
       * 
       * @since 1.0.1 
       * 
       * @param typeAliasesPackage package to scan for domain objects 
       * 
       */  
      public void setTypeAliasesPackage(String typeAliasesPackage) {  
        this.typeAliasesPackage = typeAliasesPackage;  
      }  
      
      /** 
       * Super class which domain objects have to extend to have a type alias created. 
       * No effect if there is no package to scan configured. 
       * 
       * @since 1.1.2 
       * 
       * @param typeAliasesSuperType super class for domain objects 
       * 
       */  
      public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {  
        this.typeAliasesSuperType = typeAliasesSuperType;  
      }  
      
      /** 
       * Packages to search for type handlers. 
       * 
       * @since 1.0.1 
       * 
       * @param typeHandlersPackage package to scan for type handlers 
       * 
       */  
      public void setTypeHandlersPackage(String typeHandlersPackage) {  
        this.typeHandlersPackage = typeHandlersPackage;  
      }  
      
      /** 
       * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes} 
       * 
       * @since 1.0.1 
       * 
       * @param typeHandlers Type handler list 
       */  
      public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {  
        this.typeHandlers = typeHandlers;  
      }  
      
      /** 
       * List of type aliases to register. They can be annotated with {@code Alias} 
       * 
       * @since 1.0.1 
       * 
       * @param typeAliases Type aliases list 
       */  
      public void setTypeAliases(Class<?>[] typeAliases) {  
        this.typeAliases = typeAliases;  
      }  
      
      /** 
       * If true, a final check is done on Configuration to assure that all mapped 
       * statements are fully loaded and there is no one still pending to resolve 
       * includes. Defaults to false. 
       * 
       * @since 1.0.1 
       * 
       * @param failFast enable failFast 
       */  
      public void setFailFast(boolean failFast) {  
        this.failFast = failFast;  
      }  
      
      /** 
       * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is 
       * "WEB-INF/mybatis-configuration.xml". 
       */  
      public void setConfigLocation(Resource configLocation) {  
        this.configLocation = configLocation;  
      }  
      
      /** 
       * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} 
       * configuration at runtime. 
       * 
       * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file. 
       * This property being based on Spring's resource abstraction also allows for specifying 
       * resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml". 
       */  
      public void setMapperLocations(Resource[] mapperLocations) {  
        this.mapperLocations = mapperLocations;  
      }  
      
      /** 
       * Set optional properties to be passed into the SqlSession configuration, as alternative to a 
       * {@code &lt;properties&gt;} tag in the configuration xml file. This will be used to 
       * resolve placeholders in the config file. 
       */  
      public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {  
        this.configurationProperties = sqlSessionFactoryProperties;  
      }  
      
      /** 
       * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} 
       * should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same 
       * JNDI DataSource for both. 
       * 
       * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code 
       * accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}. 
       * 
       * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not 
       * a {@code TransactionAwareDataSourceProxy}. Only data access code may work with 
       * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the 
       * underlying target {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} 
       * passed in, it will be unwrapped to extract its target {@code DataSource}. 
       * 
       */  
      public void setDataSource(DataSource dataSource) {  
        if (dataSource instanceof TransactionAwareDataSourceProxy) {  
          // If we got a TransactionAwareDataSourceProxy, we need to perform  
          // transactions for its underlying target DataSource, else data  
          // access code won't see properly exposed transactions (i.e.  
          // transactions for the target DataSource).  
          this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();  
        } else {  
          this.dataSource = dataSource;  
        }  
      }  
      
      /** 
       * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}. 
       * 
       * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By 
       * default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances. 
       * 
       */  
      public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {  
        this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;  
      }  
      
      /** 
       * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory} 
       * 
       * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases: 
       * be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction, 
       * SqlSession operations will execute SQL statements non-transactionally. 
       * 
       * <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any 
       * attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if 
       * a transaction is active. 
       * 
       * @see SpringManagedTransactionFactory 
       * @param transactionFactory the MyBatis TransactionFactory 
       */  
      public void setTransactionFactory(TransactionFactory transactionFactory) {  
        this.transactionFactory = transactionFactory;  
      }  
      
      /** 
       * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis 
       * config file. This is used only as a placeholder name. The default value is 
       * {@code SqlSessionFactoryBean.class.getSimpleName()}. 
       * 
       * @param environment the environment name 
       */  
      public void setEnvironment(String environment) {  
        this.environment = environment;  
      }  
      
      /** 
       * {@inheritDoc} 
       */  
      @Override  
      public void afterPropertiesSet() throws Exception {  
        notNull(dataSource, "Property 'dataSource' is required");  
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");  
      
        this.sqlSessionFactory = buildSqlSessionFactory();  
      }  
      
      /** 
       * Build a {@code SqlSessionFactory} instance. 
       * 
       * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a 
       * {@code SqlSessionFactory} instance based on an Reader. 
       * 
       * @return SqlSessionFactory 
       * @throws IOException if loading the config file failed 
       */  
      protected SqlSessionFactory buildSqlSessionFactory() throws IOException {  
      
        Configuration configuration;  
      
        XMLConfigBuilder xmlConfigBuilder = null;  
        if (this.configLocation != null) {  
          xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);  
          configuration = xmlConfigBuilder.getConfiguration();  
        } else {  
          if (LOGGER.isDebugEnabled()) {  
            LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");  
          }  
          configuration = new Configuration();  
          configuration.setVariables(this.configurationProperties);  
        }  
      
        if (this.objectFactory != null) {  
          configuration.setObjectFactory(this.objectFactory);  
        }  
      
        if (this.objectWrapperFactory != null) {  
          configuration.setObjectWrapperFactory(this.objectWrapperFactory);  
        }  
      
        if (hasLength(this.typeAliasesPackage)) {  
          String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,  
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
          for (String packageToScan : typeAliasPackageArray) {  
            // ThinkGem 修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动  
            try {  
                configuration.getTypeAliasRegistry().registerAliases(packageToScan,  
                        typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);  
            } catch (Exception ex) {  
                LOGGER.error("Scanned package: '" + packageToScan + "' for aliases", ex);  
                throw new NestedIOException("Scanned package: '" + packageToScan + "' for aliases", ex);  
            } finally {  
                ErrorContext.instance().reset();  
            }  
            // ThinkGem end  
            if (LOGGER.isDebugEnabled()) {  
              LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");  
            }  
          }  
        }  
      
        if (!isEmpty(this.typeAliases)) {  
          for (Class<?> typeAlias : this.typeAliases) {  
            configuration.getTypeAliasRegistry().registerAlias(typeAlias);  
            if (LOGGER.isDebugEnabled()) {  
              LOGGER.debug("Registered type alias: '" + typeAlias + "'");  
            }  
          }  
        }  
      
        if (!isEmpty(this.plugins)) {  
          for (Interceptor plugin : this.plugins) {  
            configuration.addInterceptor(plugin);  
            if (LOGGER.isDebugEnabled()) {  
              LOGGER.debug("Registered plugin: '" + plugin + "'");  
            }  
          }  
        }  
      
        if (hasLength(this.typeHandlersPackage)) {  
          String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,  
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
          for (String packageToScan : typeHandlersPackageArray) {  
            configuration.getTypeHandlerRegistry().register(packageToScan);  
            if (LOGGER.isDebugEnabled()) {  
              LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");  
            }  
          }  
        }  
      
        if (!isEmpty(this.typeHandlers)) {  
          for (TypeHandler<?> typeHandler : this.typeHandlers) {  
            configuration.getTypeHandlerRegistry().register(typeHandler);  
            if (LOGGER.isDebugEnabled()) {  
              LOGGER.debug("Registered type handler: '" + typeHandler + "'");  
            }  
          }  
        }  
      
        if (xmlConfigBuilder != null) {  
          try {  
            xmlConfigBuilder.parse();  
      
            if (LOGGER.isDebugEnabled()) {  
              LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");  
            }  
          } catch (Exception ex) {  
            throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);  
          } finally {  
            ErrorContext.instance().reset();  
          }  
        }  
      
        if (this.transactionFactory == null) {  
          this.transactionFactory = new SpringManagedTransactionFactory();  
        }  
      
        configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));  
      
        if (this.databaseIdProvider != null) {  
          try {  
            configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));  
          } catch (SQLException e) {  
            throw new NestedIOException("Failed getting a databaseId", e);  
          }  
        }  
      
        if (!isEmpty(this.mapperLocations)) {  
          for (Resource mapperLocation : this.mapperLocations) {  
            if (mapperLocation == null) {  
              continue;  
            }  
      
            try {  
              XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),  
                  configuration, mapperLocation.toString(), configuration.getSqlFragments());  
              xmlMapperBuilder.parse();  
            } catch (Exception e) {  
                // ThinkGem MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动  
                LOGGER.error("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
                throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
            } finally {  
              ErrorContext.instance().reset();  
            }  
      
            if (LOGGER.isDebugEnabled()) {  
              LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");  
            }  
          }  
            
          // ThinkGem 启动刷新MapperXML定时器(有助于开发者调试)。  
          new MapperRefresh(this.mapperLocations, configuration).run();  
            
        } else {  
          if (LOGGER.isDebugEnabled()) {  
            LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");  
          }  
        }  
      
        return this.sqlSessionFactoryBuilder.build(configuration);  
      }  
      
      /** 
       * {@inheritDoc} 
       */  
      @Override  
      public SqlSessionFactory getObject() throws Exception {  
        if (this.sqlSessionFactory == null) {  
          afterPropertiesSet();  
        }  
      
        return this.sqlSessionFactory;  
      }  
      
      /** 
       * {@inheritDoc} 
       */  
      @Override  
      public Class<? extends SqlSessionFactory> getObjectType() {  
        return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();  
      }  
      
      /** 
       * {@inheritDoc} 
       */  
      @Override  
      public boolean isSingleton() {  
        return true;  
      }  
      
      /** 
       * {@inheritDoc} 
       */  
      @Override  
      public void onApplicationEvent(ApplicationEvent event) {  
        if (failFast && event instanceof ContextRefreshedEvent) {  
          // fail-fast -> check all statements are completed  
          this.sqlSessionFactory.getConfiguration().getMappedStatementNames();  
        }  
      }  
      
    }   

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

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

    第三步:配置扫描路径

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

    (1)SpringMVC 方式

    <!-- MyBatis SqlSessionFactoryBean -->  
        <bean id="sqlSessionFactory" class="com.thinkgem.jeesite.common.mybatis.spring.SqlSessionFactoryBean">  
            <property name="dataSource" ref="dataSource"/>  
            <property name="typeAliasesPackage" value="com.thinkgem.jeesite"/>         
            <property name="mapperLocations" value="classpath*:/mappings/**/*.xml"/>  
            <property name="configLocation" value="classpath:/mybatis-config.xml"></property>  
        </bean>  

    (2)SpringBoot 方式

    @Configuration
    @MapperScan(basePackages="com.oskyhang.*.mapper", sqlSessionTemplateRef = "test1SqlSessionTemplate")
    public class DatasourceConfig {
    
        @Bean(name = "test1DataSource")
        @ConfigurationProperties(prefix = "spring.datasource")
        @Primary
        public DataSource testDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "test1SqlSessionFactory")
        @Primary
        public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            // TODO
            // 加载全局的配置文件
            bean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:sqlMapConfig.xml"));
    
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:com/oskyhang/system/mapper/*.xml");
            bean.setMapperLocations(resources);
    
            return bean.getObject();
        }
    
        @Bean(name = "test1TransactionManager")
        @Primary
        public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    
        @Bean(name = "test1SqlSessionTemplate")
        @Primary
        public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    
    }
  • 相关阅读:
    jvm基本结构和解析
    多态的意思
    java中对象的简单解读
    double类型和int类型的区别
    python 解析xml文件
    win10不能映射Ubuntu共享文件
    Qt程序打包
    Ubuntu boot分区文件误删,系统无法启动,怎么解
    ubuntu Boot空间不够问题“The volume boot has only 5.1MB disk space remaining”
    Ubuntu 分辨率更改 xrandr Failed to get size of gamma for output default
  • 原文地址:https://www.cnblogs.com/oskyhg/p/8587701.html
Copyright © 2011-2022 走看看