Pārlūkot izejas kodu

mapper动态加载

nieqiurong 9 gadi atpakaļ
vecāks
revīzija
8ae14b16b5

+ 18 - 10
mybatis-plus/src/main/java/com/baomidou/mybatisplus/MybatisConfiguration.java

@@ -15,24 +15,23 @@
  */
 package com.baomidou.mybatisplus;
 
-import java.util.logging.Logger;
-
-import org.apache.ibatis.mapping.MappedStatement;
-import org.apache.ibatis.session.Configuration;
-
 import com.baomidou.mybatisplus.mapper.AutoSqlInjector;
 import com.baomidou.mybatisplus.mapper.DBType;
 import com.baomidou.mybatisplus.mapper.ISqlInjector;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.Configuration;
+
+import java.util.logging.Logger;
 
 /**
  * <p>
  * replace default Configuration class
  * </p>
- * 
+ *
  * @author hubin
  * @Date 2016-01-23
  */
-public class MybatisConfiguration extends Configuration {
+public class MybatisConfiguration extends Configuration{
 
 	protected final Logger logger = Logger.getLogger("MybatisConfiguration");
 
@@ -51,6 +50,11 @@ public class MybatisConfiguration extends Configuration {
 	 */
 	public static ISqlInjector SQL_INJECTOR = new AutoSqlInjector();
 
+	/*
+	 * 是否刷新mapper
+	 */
+	public static boolean IS_REFRESH = false;
+
 	/**
 	 * 初始化调用
 	 */
@@ -71,12 +75,16 @@ public class MybatisConfiguration extends Configuration {
 	@Override
 	public void addMappedStatement(MappedStatement ms) {
 		logger.fine(" addMappedStatement: " + ms.getId());
-		if (this.mappedStatements.containsKey(ms.getId())) {
+		if(IS_REFRESH){
+			this.mappedStatements.remove(ms.getId());
+		}else{
+			if (this.mappedStatements.containsKey(ms.getId())){
 			/*
 			 * 说明已加载了xml中的节点; 忽略mapper中的SqlProvider数据
 			 */
-			logger.severe("mapper[" + ms.getId() + "] is ignored, because it's exists, maybe from xml file");
-			return;
+				logger.severe("mapper[" + ms.getId() + "] is ignored, because it's exists, maybe from xml file");
+				return;
+			}
 		}
 		super.addMappedStatement(ms);
 	}

+ 233 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/refresh/MapperRefresh.java

@@ -0,0 +1,233 @@
+package com.baomidou.mybatisplus.refresh;
+
+import com.baomidou.mybatisplus.MybatisConfiguration;
+import org.apache.ibatis.binding.MapperRegistry;
+import org.apache.ibatis.builder.xml.XMLMapperBuilder;
+import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
+import org.apache.ibatis.executor.ErrorContext;
+import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.parsing.XPathParser;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.core.NestedIOException;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.util.ResourceUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.logging.Logger;
+
+/**
+ * Created by nieqiurong on 2016/8/25 16:13.
+ */
+public class MapperRefresh implements Runnable{
+    protected final Logger logger = Logger.getLogger(MapperRefresh.class.getName());
+    private SqlSessionFactory sqlSessionFactory;
+    private Resource[] mapperLocations;
+    private Long beforeTime = 0L;
+    private Configuration configuration;
+    /**
+     *  是否开启刷新mapper
+     */
+    private boolean enabled;
+    /**
+     * xml文件目录
+     */
+    private Set<String> fileSet;
+    /**
+     * 延迟加载时间
+     */
+    private int delaySeconds = 10;
+    /**
+     * 刷新间隔时间
+     */
+    private int sleepSeconds = 20;
+    /**
+     * 记录jar包存在的mapper
+     */
+    private static Map<String,List<Resource>> jarMapper = new HashMap<String, List<Resource>>();
+
+    public MapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, int delaySeconds, int sleepSeconds) {
+        this.mapperLocations = mapperLocations;
+        this.sqlSessionFactory = sqlSessionFactory;
+        this.delaySeconds = delaySeconds;
+        this.enabled = true;
+        this.sleepSeconds = sleepSeconds;
+        this.run();
+    }
+
+    public MapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory) {
+        this.mapperLocations = mapperLocations;
+        this.sqlSessionFactory = sqlSessionFactory;
+        this.run();
+
+    }
+
+    public MapperRefresh(Resource[] mapperLocations, SqlSessionFactory sqlSessionFactory, boolean enabled) {
+        this.mapperLocations = mapperLocations;
+        this.sqlSessionFactory = sqlSessionFactory;
+        this.enabled = enabled;
+        this.run();
+    }
+
+    public void run() {
+        beforeTime = System.currentTimeMillis();
+        if (enabled) {
+            final MapperRefresh runnable = this;
+            new Thread(new Runnable(){
+                @Override
+                public void run() {
+                    if (fileSet == null) {
+                        fileSet = new HashSet<String>();
+                        for (Resource mapperLocation : mapperLocations) {
+                            try {
+                                if (ResourceUtils.isJarURL(mapperLocation.getURL())){
+                                    String key = new UrlResource(ResourceUtils.extractJarFileURL(mapperLocation.getURL())).getFile().getPath();
+                                    fileSet.add(key);
+                                    if(jarMapper.get(key)!=null){
+                                        jarMapper.get(key).add(mapperLocation);
+                                    }else{
+                                        List<Resource> resourcesList = new ArrayList<Resource>();
+                                        resourcesList.add(mapperLocation);
+                                        jarMapper.put(key,resourcesList);
+                                    }
+                                } else {
+                                    fileSet.add(mapperLocation.getFile().getPath());
+                                }
+                            } catch (IOException ioException) {
+                                ioException.printStackTrace();
+                            }
+                        }
+                    }
+                    try {
+                        Thread.sleep(delaySeconds * 1000);
+                    } catch (InterruptedException interruptedException) {
+                        interruptedException.printStackTrace();
+                    }
+                    while (true) {
+                        try {
+                            for (String filePath : fileSet){
+                                File file = new File(filePath);
+                                if(file!=null&&file.isFile()&&file.lastModified()>beforeTime){
+                                    MybatisConfiguration.IS_REFRESH = true;
+                                    List<Resource> removeList = jarMapper.get(filePath);
+                                    if(removeList!=null&&!removeList.isEmpty()){//如果是jar包中的xml,将刷新jar包中存在的所有xml,后期再修改加载jar中修改过后的xml
+                                        for(Resource resource:removeList){
+                                            runnable.refresh(resource);
+                                        }
+                                    }else{
+                                        runnable.refresh(new FileSystemResource(file));
+                                    }
+                                }
+                            }
+                            if(MybatisConfiguration.IS_REFRESH)beforeTime = System.currentTimeMillis();
+                            MybatisConfiguration.IS_REFRESH = false;
+                        } catch (Exception exception) {
+                            exception.printStackTrace();
+                        }
+                        try {
+                            Thread.sleep(sleepSeconds * 1000);
+                        } catch (InterruptedException interruptedException) {
+                            interruptedException.printStackTrace();
+                        }
+
+                    }
+                }
+            },"mybatis-plus MapperRefresh").start();
+        }
+    }
+
+    /**
+     * 刷新mapper
+     * @throws Exception
+     */
+    private void refresh(Resource resource) throws Exception {
+        this.configuration = sqlSessionFactory.getConfiguration();
+        boolean isSupper = configuration.getClass().getSuperclass()==Configuration.class;
+        try {
+            Field loadedResourcesField = isSupper?configuration.getClass().getSuperclass().getDeclaredField("loadedResources"):configuration.getClass().getDeclaredField("loadedResources");
+            loadedResourcesField.setAccessible(true);
+            Set loadedResourcesSet = ((Set) loadedResourcesField.get(configuration));
+            XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(),new XMLMapperEntityResolver());
+            XNode context = xPathParser.evalNode("/mapper");
+            String namespace = context.getStringAttribute("namespace");
+            Field field = MapperRegistry.class.getDeclaredField("knownMappers");
+            field.setAccessible(true);
+            Map mapConfig = (Map) field.get(configuration.getMapperRegistry());
+            mapConfig.remove(Resources.classForName(namespace));
+            loadedResourcesSet.remove(resource.toString());
+            configuration.getCacheNames().remove(namespace);
+            cleanParameterMap(context.evalNodes("/mapper/parameterMap"),namespace);
+            cleanResultMap(context.evalNodes("/mapper/resultMap"),namespace);
+            cleanKeyGenerators(context.evalNodes("insert|update"),namespace);
+            cleanSqlElement(context.evalNodes("/mapper/sql"),namespace);
+            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(), sqlSessionFactory.getConfiguration(),   //注入的sql先不进行处理了
+                    resource.toString(), sqlSessionFactory.getConfiguration().getSqlFragments());
+            xmlMapperBuilder.parse();
+            logger.info("refresh:"+resource+",success!");
+        } catch (Exception e) {
+            throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);
+        } finally {
+            ErrorContext.instance().reset();
+        }
+    }
+
+    /**
+     * 清理parameterMap
+     * @param list
+     * @param namespace
+     */
+    private void cleanParameterMap(List<XNode> list,String namespace){
+        for (XNode parameterMapNode : list) {
+            String id = parameterMapNode.getStringAttribute("id");
+            configuration.getParameterMaps().remove(namespace+"."+id);
+        }
+    }
+
+    /**
+     * 清理resultMap
+     * @param list
+     * @param namespace
+     */
+    private void cleanResultMap(List<XNode> list,String namespace){
+        for (XNode resultMapNode : list) {
+            String id = resultMapNode.getStringAttribute("id",
+                    resultMapNode.getValueBasedIdentifier());
+            configuration.getResultMapNames().remove(id);
+            configuration.getResultMapNames().remove(namespace+"."+id);
+        }
+    }
+
+    /**
+     * 清理selectKey
+     * @param list
+     * @param namespace
+     */
+    private void cleanKeyGenerators(List<XNode> list,String namespace){
+        for (XNode context : list) {
+            String id = context.getStringAttribute("id");
+            configuration.getKeyGeneratorNames().remove(id+SelectKeyGenerator.SELECT_KEY_SUFFIX);
+            configuration.getKeyGeneratorNames().remove(namespace+"."+id+SelectKeyGenerator.SELECT_KEY_SUFFIX);
+        }
+    }
+
+    /**
+     * 清理sql节点缓存
+     * @param list
+     * @param namespace
+     */
+    private void cleanSqlElement(List<XNode> list,String namespace){
+        for (XNode context : list) {
+            String id = context.getStringAttribute("id");
+            configuration.getSqlFragments().remove(id);
+            configuration.getSqlFragments().remove(namespace+"."+id);
+        }
+    }
+
+}

+ 50 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/refresh/MapperRefreshTest.java

@@ -0,0 +1,50 @@
+package com.baomidou.mybatisplus.test.refresh;
+
+import com.baomidou.mybatisplus.MybatisSessionFactoryBuilder;
+import com.baomidou.mybatisplus.plugins.pagination.Pagination;
+import com.baomidou.mybatisplus.refresh.MapperRefresh;
+import com.baomidou.mybatisplus.test.mysql.MySqlInjector;
+import com.baomidou.mybatisplus.test.mysql.UserMapper;
+import com.baomidou.mybatisplus.test.mysql.UserMapperTest;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Created by nieqiurong on 2016/8/26 6:36.
+ */
+public class MapperRefreshTest {
+    public static void main(String[] args) throws IOException, InterruptedException {
+
+        InputStream in = UserMapperTest.class.getClassLoader().getResourceAsStream("mysql-config.xml");
+        MybatisSessionFactoryBuilder mf = new MybatisSessionFactoryBuilder();
+        mf.setSqlInjector(new MySqlInjector());
+        Resource[] resource = new ClassPathResource[]{new ClassPathResource("mysql/UserMapper.xml")};
+        SqlSessionFactory sessionFactory = mf.build(in);
+        new MapperRefresh(resource,sessionFactory,0,5);
+        boolean isReturn = false;
+        SqlSession session=null;
+        while (!isReturn){
+            try {
+                session = sessionFactory.openSession();
+                UserMapper userMapper = session.getMapper(UserMapper.class);
+                userMapper.selectListRow(new Pagination(1,10));
+                resource[0].getFile().setLastModified(System.currentTimeMillis());
+                session.commit();
+                session.close();
+                Thread.sleep(5000);
+            }catch (Exception e){
+                e.printStackTrace();
+//                isReturn = true;
+            }finally {
+                if(session!=null)session.close();
+                Thread.sleep(5000);
+            }
+        }
+        System.exit(0);
+    }
+}