SpringMVC+Mybatis+热部署mapper文件

SpringMVC+Mybatis+热部署mapper文件

每天都是从网上找别人分享出来的好东西纳入囊中,今天自己也想分享一下自己的一些东西给大家。

一直在用SSH框架,没办法现在的主流就是SpringMvc+Mybatis,所以也研究了一下。看了几天网上分享的精髓,恕小弟我愚昧,搞了好久才弄明白其中的奥妙,不过我觉得自己懂了还不够深刻,必须拿出来和大家分享,让大家也能看懂才是真正的懂了。

我尽量写的简单点通俗易懂,只要是有点java基础就能看懂这篇文章是我的目标。

我们老大最近让了解markDown,写标题的时候正好看到有‘切换markDown编辑器’,直接开干,之前真的一点也不了解markDown。

(奥利给,我次有内,为给呀,老铁没毛病,今天又是一个楼面,100来平方,轻松拿下,干就完了!!)不要问我这句话啥意思,最近快手上有个雷哥,因这句口头禅火了。废话是不是有点太多了?好吧,我错了步入正题!!!

###我发现一个问题,也可能是我自己的问题,就是在看别人分享出来的资源的时候,总是跟着资源一步一步做,然后突然卡壳,要不就是报错要不就是各种纠结,瞬间继续看下去的心情就没了,这里建议大家耐心点,哪里卡壳了,就好好研究一下,当你迈过这个坎后,你肯定会收获更多

##开发工具
Myeclipse2014jdk1.7tomcat7,数据库我用的是oracle,伙计们这几个东西都先给我准备好了,马上开始打仗了。什么?这些东西你都不知道?用我们老家的口头禅来说“外日他dei儿,你给我去求吧”,这是作为一个程序员必须知道的,就像你打仗需要用枪一个道理!

##愤怒感言
每次跟着网上的资料学习,都会被一大堆的各种jar问题包搞的晕头转向,我总觉得这个是最浪费时间的,而且是能让人发疯的节奏!所以,这里我就先来拯救一下正在看我文章的小伙伴们吧,谁让你我最亲呢!我这里把所有jar包先贴出来,然后附上下载地址,请小伙伴们先不要问jar包的具体用途,先下载用了再说。
SpringMVC+Mybatis+热部署mapper文件_第1张图片

下载链接:http://download.csdn.net/detail/chunge48596/9709211
好了,小伙伴,我们离成功就差一步了,要不咱们先喝杯茶聊聊妹子聊聊人生吧。Set Fire to the Rain好听,你值得拥有

##项目结构
接下来我先把项目结构贴出来,有心的小伙伴可能会发现怎么看着有点眼熟?没办法,我也是从网上找的资源拼凑的,大家凑合着看吧。还想咋滴?不行咱们来打一架?哼~
SpringMVC+Mybatis+热部署mapper文件_第2张图片

卧槽,刚才手机振动了一下,打开一看居然发工资了,我这小心脏扑通扑通的,老板:给我来个鸡蛋灌饼,加2个蛋!!!!老子有钱

##开始盖楼
在Package Explorer空白处右键 > new > Web Project

SpringMVC+Mybatis+热部署mapper文件_第3张图片

next>next>各种next,最后Finish,然后出现如下结构

SpringMVC+Mybatis+热部署mapper文件_第4张图片

###然后咱们把上面讲到的jar包全部放进项目里,第一步打开项目家大门,第二部把jar包拿起来,第三部使劲把这些jar包扔进项目家里!!!
不逗你们啦,我是这样弄的,去Workspaces找到你刚建立好的项目,然后把所有jar包放进lib目录下,我的项目路径 > D:\Workspaces2014\spring-mvc\WebRoot\WEB-INF\lib

不知道自己的Workspaces项目路径在哪?我勒个擦,我的小哥,你确定你是个程序员吗?我再教教你,谁让我就是这么好呢!!!以后给我记住了,八嘎
右键你的项目名称 > Properties > Resource 然后你看到了什么?哦~ ~ ~ ~ ~知道了,在这里啊!

SpringMVC+Mybatis+热部署mapper文件_第5张图片

###下面就是数据库连接了,建立JDBC属性文件,项目都是这样,把数据库配置独立出来,这样是极好的

右键src > new > File,名字取名叫 jdbc.properties > Finish
右键 jdbc.properties > Properties

SpringMVC+Mybatis+热部署mapper文件_第6张图片

将 jdbc.properties 文件编码修改为UTF-8

jdbc.properties 里面复制下面数据库配置,这里请注意,我用的是oracle 用其他数据库的小伙伴要注意哦!当然这里需要配置你自己的数据库配置,这里如果你不会,那么我建议去问一下旁边的老师,或者同事,请人家吃麻辣烫。你想问我啊?也行,我要喝16块钱一碗的方中山胡辣汤,嘎嘎!

jdbc.driver=oracle.jdbc.OracleDriver

#test environment
jdbc.url=jdbc\:oracle\:thin\:@192.111.111.111\:1521\:oraga1
jdbc.username=bugaosuni
jdbc.password=nicai

#jdbc.maxActive=1000
#jdbc.maxIdle=300
#jdbc.maxWait=60000
#defaultAutoCommit=false
#removeAbandoned=true
#removeAbandonedTimeout=600
#jdbc.validationQuery=select 1 from dual
#jdbc.testOnBorrow=true

只是一个简单的一个SpringMvc例子,所以上面很多数据库连接池的配置我都先注释掉了,想要强大的伙伴自己去研究研究吧!

###创建log4j.properties文件,里面的注释异常的详细,不多说了,直接贴出代码
###记得把把文件格式转换成utf-8

### Log4j配置 ###
### 与Spring结合需要在web.xml中指定此文件位置,并添加监听器 ###
#定义log4j的输出级别和输出目的地(目的地可以自定义名称,和后面的对应)
#[ level ] , appenderName1 , appenderName2
log4j.rootLogger=DEBUG,console,file

#-----------------------------------#
#1 定义日志输出目的地为控制台
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
####可以灵活地指定日志输出格式,下面一行是指定具体的格式 ###
#%c: 输出日志信息所属的类目,通常就是所在类的全名
#%m: 输出代码中指定的消息,产生的日志具体信息 
#%n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#-----------------------------------#
#2 文件大小到达指定尺寸的时候产生一个新的文件 
log4j.appender.file = org.apache.log4j.RollingFileAppender
#日志文件输出目录
log4j.appender.file.File=logs/ssm.log
#定义文件最大大小
log4j.appender.file.MaxFileSize=10mb
###输出日志信息###
#最低级别
log4j.appender.file.Threshold=ERROR
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#-----------------------------------#
#4 mybatis 输出SQL语句部分
log4j.logger.org.mybatis=DEBUG
#log4j.logger.cn.tibet.cas.dao=DEBUG
#log4j.logger.org.mybatis.common.jdbc.SimpleDataSource=DEBUG#
#log4j.logger.org.mybatis.common.jdbc.ScriptRunner=DEBUG#
#log4j.logger.org.mybatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG#
#log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG 

###建立spring-mybatis.xml配置文件,很重要的一步哦,同学们要给我竖起耳朵听着,瞪大眼睛看着

右键src > new > File,名字取名叫 spring-mybatis.xml > Finish,记得看一下文件编码格式,不是UTF-8的修改一下
spring-mybatis.xml配置文件,主要的就是自动扫描,自动注入,配置数据库。

    
  
      
 	  
     
    
      
    
      
      
       
        
          
          
          
     	  
      
      
      
      
          
          
          
      
  
      
      
          
          
      
  
      
      
          
    
    
 

###上面的步骤完成后,我们用MyBatis Generator自动创建代码(dao,entity,mapper),这步很牛逼,当你实战的时候能为你省下不少时间,跟网上的大神学的

在D盘,或者随便哪个盘里面创建一个文件夹,名字自取。文件夹里面的内容如下:SpringMVC+Mybatis+热部署mapper文件_第7张图片

属实操蛋了,写完预览了一下,发现博客都看不清楚包的名字,我在列一下包名和文件名:
generatorConfig.xml
mybatis-3.2.7.jar
mybatis-generator-core-1.3.2.jar
mysql-connector-java-5.1.25-bin.jar
ojdbc14.jar

generatorConfig.xml内容如下

    
    

	
	
    
	
	
	
	
    
        
            
			
            
        
		
        
		
		
		
		
        
        
            
        
		
        
            
            
        
		
        
            
        
		
        
            
        
		
        

以上这些都弄好之后,用控制台进入当前目录下,按照我的步骤来:

1、按Ctrl + r 输入 cmd 回车,进入控制台
2、输入 d: 回车(这里取决于你的文件夹在哪个盘里)
3、输入 cd 文件夹名字 回车(注意cd后面有个空格)
4、输入 Java -jar mybatis-generator-core-1.3.2.jar -configfile generatorConfig.xml -overwrite回车

然后稍等一下,出现以下提示及表示成功
SpringMVC+Mybatis+热部署mapper文件_第8张图片

这里要注意,如果执行报错,可能是因为 generatorConfig.xml 编码格式不对,用记事本打开generatorConfig.xml然后另存为ANSI格式,覆盖之前的generatorConfig.xml,再进行执行命令
然后你就可以去看看src文件夹里面的东西了,这里需要注意下生成的mapper文件,里面好像有点重复了,耐心点看看是否重复了,有重复就把重复的部分去掉即可

##接下来我们进行测试我们的代码是否能成功运行
将生成的文件复制到我们的项目里面,如下图:

SpringMVC+Mybatis+热部署mapper文件_第9张图片

不是说贴进来就行了,肯定会报错,把错误改改,就是一些包的位置和引入错误,改好之后需要注意一下mapper.xml文件,我接下来给伙伴们说一下mapper.xml文件各种关键字对应的位置和意思





  	
  
    
    
    
    
    
    
    
    
    
  
  
  
    ID, CATEGORY_LEVEL, PARENT_ID, CATEGORY_NAME, CATEGORY_EXPLAIN, PROFIT_MARGIN, CREATE_DATE, 
    UPDATE_DATE, REMARK
  
  
  
  
  

太费劲了,我只贴出来一部分代码,后面那些更新和删除就不贴出来了,就简单的给大家演示一下查询,我相信在我的调教下,这个查询会了之后,其他的比葫芦画瓢,你绝对能学会~

###接下来我们就可以进行测试了,创建test、service、serviceImpl三个文件,下面贴出来代码

SpringMVC+Mybatis+热部署mapper文件_第10张图片

###industryService:

package com.alibaba.service;

import com.alibaba.entity.industry;

public interface industryService {
	//根据ID查询行业信息
	public industry selectByPrimaryKey(Integer id);
	
}

###industryServiceImpl:

package com.alibaba.service.serviceImpl;

import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.alibaba.dao.industryMapper;
import com.alibaba.entity.industry;
import com.alibaba.service.industryService;

@Service
public class industryServiceImpl implements industryService{
	@Resource
	private industryMapper industryDao;
	
	//根据id查询行业信息
	public industry selectByPrimaryKey(Integer id){
		return industryDao.selectByPrimaryKey(id);
	}
}

###test:

package com.alibaba.controller;

import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.alibaba.entity.industry;
import com.alibaba.service.industryService;

@RunWith(SpringJUnit4ClassRunner.class)     //表示继承了SpringJUnit4ClassRunner类 
@ContextConfiguration(locations = {"classpath:spring-mybatis.xml"}) 
public class test {
	private static Logger logger = Logger.getLogger(test.class); 
	@Resource
	private industryService iService;
	@Test
	public void test2(){
		industry i = iService.selectByPrimaryKey(3);
		System.out.println("行业说明:"+i.getCategoryExplain());
		logger.info("行业说明:"+i.getCategoryExplain());
	}
}

一切准备就绪,打开test文件,右键 > Run As > JUnit Test,看控制台有没有输出你的数据,有数据,说明说明你已炼成葵花宝典第一式

SpringMVC+Mybatis+热部署mapper文件_第11张图片

##下面创建spring-mvc.xml文件,SpringMvc的核心
###直接贴出配置

  
 
                         
      
      
      
      
          
              
                text/html;charset=UTF-8  
              
          
      
      
      
          
              
                   
              
          
      
      
      
          
          
          
      
      
      
        
          
            
          
            
          
            
       
  
  

###在WEB-INF下创建web.xml,配置如下:

  
  
    Archetype Created Web Application  
    
      
      
        contextConfigLocation  
        classpath:spring-mybatis.xml  
      
      
      
        encodingFilter  
        org.springframework.web.filter.CharacterEncodingFilter  
        true  
          
            encoding  
            UTF-8  
          
      
      
        encodingFilter  
        /*  
      
    
      
      
        org.springframework.web.context.ContextLoaderListener  
      
    
      
      
        org.springframework.web.util.IntrospectorCleanupListener  
     
    
      
      
        SpringMVC  
        org.springframework.web.servlet.DispatcherServlet  
          
            contextConfigLocation  
            classpath:spring-mvc.xml  
          
        1  
        true  
      
      
        SpringMVC  
          
        *.do  
     
     
      
        /index.jsp  
     
  

###在controller包里创建industryController(名字自定义):

package com.alibaba.controller;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.entity.industry;
import com.alibaba.service.industryService;

@Controller
@RequestMapping("/industry")
public class industryController {
	private static Logger logger = Logger.getLogger(industryController.class); 
	private industry indu;
	@Resource 
	private industryService iservice;
	
	@RequestMapping("/queryIndustry")
	public String queryIndustryById(HttpServletRequest request,String id){
		/*接收前台传过来的参数,可以直接接收,不需要任何多余的代码,就直接在方法的参数里面定义好就行,牛逼思密达*/
		/*这里需要注意,如果参数只有一个,url传过来的参数名称和controller方法里面的参数名称不对应也能接收到值;如果是多个参数,那就必须一一对应,不然会接收不到值*/
		indu = iservice.selectByPrimaryKey(Integer.parseInt(id));
		request.getSession().setAttribute("industry", indu);
		return "showIndustry";
	}
}

###index.jsp,如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>



  
    
    
    My JSP 'index.jsp' starting page
	
	
	    
	
	
	
  
  
  
    点击查询行业信息
  


###在WEB-INF下创建pages文件夹,然后创建showIndustry.jsp,名字不是随便起的,跟你controller方法返回的字符串要对应
SpringMVC+Mybatis+热部署mapper文件_第12张图片

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>



  
    
    
    My JSP 'index.jsp' starting page
	
	
	    
	
	
	
  
  
  
    
行业名称 ${industry.categoryName }
行业说明 ${industry.categoryExplain }

##差不多要结束了,我们把项目部署到tomcat7下,然后运行项目

SpringMVC+Mybatis+热部署mapper文件_第13张图片

打开浏览器,输入:http://localhost:8080/spring-mvc

SpringMVC+Mybatis+热部署mapper文件_第14张图片

“点击查询行业信息”

SpringMVC+Mybatis+热部署mapper文件_第15张图片

#大功告成,艾玛,真累,到这里SpringMvc伙伴们就差不多学会了,我建议大家还是多看几遍过程,哪个地方不会去搜一下,或者你只需要一碗胡辣汤贿赂贿赂我,我就勉为其难的给你讲讲撒,哈哈,开个玩笑啦

#已经学会的同学可能会发现一个问题,就是每次我们修改一下mapper文件,就需要重启一下项目。
#项目小了还好,如果以后项目大了,修改一下mapper,重启一下项目,这尼玛蛋都碎一地了
#所以,又费老鼻子劲去找方法,最终功夫不负有心人,接下来,就给大家讲讲自动加载mapper文件的方法
#这个东西挺有意思,只要发现任何mapper文件被修改,就会重新加载被修改的mapper文件,**只加载被修改的mapper文件,只加载被修改的mapper文件,只加载被修改的mapper文件重要的事情说三遍!!!**网上还有些方法也能实现自动加载mapper文件,不过我觉得麻烦,而且每次修改一个mapper文件,就需要重新加载所有的mapper文件,效率上也不会太好,废话不多说,直接开始

##在src下创建util包,然后创建一个类,名字:MapperRefresh

SpringMVC+Mybatis+热部署mapper文件_第16张图片

然后把下面这一大坨代码贴进MapperRefresh.java,里面的代码说实话我也没怎么看,说不定也看不懂,先拿起来用吧

package com.alibaba.util;

import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileNotFoundException;  
import java.io.InputStream;  
import java.lang.reflect.Field;  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
import java.util.Properties;  
import java.util.Set;  
  
import org.apache.commons.lang3.StringUtils;  
import org.apache.ibatis.builder.xml.XMLMapperBuilder;  
import org.apache.ibatis.executor.ErrorContext;  
import org.apache.ibatis.session.Configuration;  
import org.apache.log4j.Logger;  
import org.springframework.core.NestedIOException;  
import org.springframework.core.io.Resource;  
  
import com.google.common.collect.Sets;  
  
/** 
 * 刷新MyBatis Mapper XML 线程 
 * @author ThinkGem 这个是原著的作者,我只是直接拿来用了,原著莫怪
 * @version 2016-5-29 
 */  
public class MapperRefresh implements java.lang.Runnable {  
  
    public static Logger log = Logger.getLogger(MapperRefresh.class);  
  
    private static String filename = "/mybatis-refresh.properties";  
    private static Properties prop = new Properties();  
  
    private static boolean enabled;         // 是否启用Mapper刷新线程功能  
    private static boolean refresh;         // 刷新启用后,是否启动了刷新线程  
      
    private Set location;         // Mapper实际资源路径  
      
    private Resource[] mapperLocations;     // Mapper资源路径  
    private Configuration configuration;        // MyBatis配置对象  
      
    private Long beforeTime = 0L;           // 上一次刷新时间  
    private static int delaySeconds;        // 延迟刷新秒数  
    private static int sleepSeconds;        // 休眠时间  
    private static String mappingPath;      // xml文件夹匹配字符串,需要根据需要修改  
  
    static {  
          
        try {  
            prop.load(MapperRefresh.class.getResourceAsStream(filename));  
        } catch (Exception e) {  
            e.printStackTrace();  
            System.out.println("Load mybatis-refresh “"+filename+"” file error.");  
        }  
  
        enabled = "true".equalsIgnoreCase(getPropString("enabled"));  
          
        delaySeconds = getPropInt("delaySeconds");  
        sleepSeconds = getPropInt("sleepSeconds");  
        mappingPath = getPropString("mappingPath");  
  
        delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;  
        sleepSeconds = sleepSeconds == 0 ? 3 : sleepSeconds;  
        mappingPath = StringUtils.isBlank(mappingPath) ? "mappings" : mappingPath;  
  
        log.debug("[enabled] " + enabled);  
        log.debug("[delaySeconds] " + delaySeconds);  
        log.debug("[sleepSeconds] " + sleepSeconds);  
        log.debug("[mappingPath] " + mappingPath);  
    }  
  
    public static boolean isRefresh() {  
        return refresh;  
    }  
  
    public MapperRefresh(Resource[] mapperLocations, Configuration configuration) {  
        this.mapperLocations = mapperLocations;  
        this.configuration = configuration;  
    }  
  
    @Override  
    public void run() {  
  
        beforeTime = System.currentTimeMillis();  
  
        log.debug("[location] " + location);  
        log.debug("[configuration] " + configuration);  
  
        if (enabled) {  
            // 启动刷新线程  
            final MapperRefresh runnable = this;  
            new Thread(new java.lang.Runnable() {  
                @Override  
                public void run() {  
                      
                    if (location == null){  
                        location = Sets.newHashSet();  
                        log.debug("MapperLocation's length:" + mapperLocations.length);  
                        for (Resource mapperLocation : mapperLocations) {  
                            String s = mapperLocation.toString().replaceAll("\\\\", "/");  
                            s = s.substring("file [".length(), s.lastIndexOf(mappingPath) + mappingPath.length());  
                            if (!location.contains(s)) {  
                                location.add(s);  
                                log.debug("Location:" + s);  
                            }  
                        }  
                        log.debug("Locarion's size:" + location.size());  
                    }  
  
                    try {  
                        Thread.sleep(delaySeconds * 1000);  
                    } catch (InterruptedException e2) {  
                        e2.printStackTrace();  
                    }  
                    refresh = true;  
  
                    System.out.println("========= Enabled refresh mybatis mapper =========");  
  
                    while (true) {  
                        try {  
                            for (String s : location) {  
                                runnable.refresh(s, beforeTime);  
                            }  
                        } catch (Exception e1) {  
                            e1.printStackTrace();  
                        }  
                        try {  
                            Thread.sleep(sleepSeconds * 1000);  
                        } catch (InterruptedException e) {  
                            e.printStackTrace();  
                        }  
  
                    }  
                }  
            }, "MyBatis-Mapper-Refresh").start();  
        }  
    }  
  
    /** 
     * 执行刷新 
     * @param filePath 刷新目录 
     * @param beforeTime 上次刷新时间 
     * @throws NestedIOException 解析异常 
     * @throws FileNotFoundException 文件未找到 
     * @author ThinkGem 
     */  
    @SuppressWarnings({ "rawtypes", "unchecked" })  
    private void refresh(String filePath, Long beforeTime) throws Exception {  
  
        // 本次刷新时间  
        Long refrehTime = System.currentTimeMillis();  
  
        // 获取需要刷新的Mapper文件列表  
        List fileList = this.getRefreshFile(new File(filePath), beforeTime);  
        if (fileList.size() > 0) {  
            log.debug("Refresh file: " + fileList.size());  
        }  
        for (int i = 0; i < fileList.size(); i++) {  
            InputStream inputStream = new FileInputStream(fileList.get(i));  
            String resource = fileList.get(i).getAbsolutePath();  
            try {  
                  
                // 清理原有资源,更新为自己的StrictMap方便,增量重新加载  
                String[] mapFieldNames = new String[]{  
                    "mappedStatements", "caches",  
                    "resultMaps", "parameterMaps",  
                    "keyGenerators", "sqlFragments"  
                };  
                for (String fieldName : mapFieldNames){  
                    Field field = configuration.getClass().getDeclaredField(fieldName);  
                    field.setAccessible(true);  
                    Map map = ((Map)field.get(configuration));  
                    if (!(map instanceof StrictMap)){  
                        Map newMap = new StrictMap(StringUtils.capitalize(fieldName) + "collection");  
                        for (Object key : map.keySet()){  
                            try {  
                                newMap.put(key, map.get(key));  
                            }catch(IllegalArgumentException ex){  
                                newMap.put(key, ex.getMessage());  
                            }  
                        }  
                        field.set(configuration, newMap);  
                    }  
                }  
                  
                // 清理已加载的资源标识,方便让它重新加载。  
                Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");  
                loadedResourcesField.setAccessible(true);  
                Set loadedResourcesSet = ((Set)loadedResourcesField.get(configuration));  
                loadedResourcesSet.remove(resource);  
                  
                //重新编译加载资源文件。  
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, configuration,   
                        resource, configuration.getSqlFragments());  
                xmlMapperBuilder.parse();  
            } catch (Exception e) {  
                throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);  
            } finally {  
                ErrorContext.instance().reset();  
            }  
            System.out.println("Refresh file: " + mappingPath + StringUtils.substringAfterLast(fileList.get(i).getAbsolutePath(), mappingPath));  
            if (log.isDebugEnabled()) {  
                log.debug("Refresh file: " + fileList.get(i).getAbsolutePath());  
                log.debug("Refresh filename: " + fileList.get(i).getName());  
            }  
        }  
        // 如果刷新了文件,则修改刷新时间,否则不修改  
        if (fileList.size() > 0) {  
            this.beforeTime = refrehTime;  
        }  
    }  
      
    /** 
     * 获取需要刷新的文件列表 
     * @param dir 目录 
     * @param beforeTime 上次刷新时间 
     * @return 刷新文件列表 
     */  
    private List getRefreshFile(File dir, Long beforeTime) {  
        List fileList = new ArrayList();  
  
        File[] files = dir.listFiles();  
        if (files != null) {  
            for (int i = 0; i < files.length; i++) {  
                File file = files[i];  
                if (file.isDirectory()) {  
                    fileList.addAll(this.getRefreshFile(file, beforeTime));  
                } else if (file.isFile()) {  
                    if (this.checkFile(file, beforeTime)) {  
                        fileList.add(file);  
                    }  
                } else {  
                    System.out.println("Error file." + file.getName());  
                }  
            }  
        }  
        return fileList;  
    }  
  
    /** 
     * 判断文件是否需要刷新 
     * @param file 文件 
     * @param beforeTime 上次刷新时间 
     * @return 需要刷新返回true,否则返回false 
     */  
    private boolean checkFile(File file, Long beforeTime) {  
        if (file.lastModified() > beforeTime) {  
            return true;  
        }  
        return false;  
    }  
  
    /** 
     * 获取整数属性 
     * @param key 
     * @return 
     */  
    private static int getPropInt(String key) {  
        int i = 0;  
        try {  
            i = Integer.parseInt(getPropString(key));  
        } catch (Exception e) {  
        }  
        return i;  
    }  
  
    /** 
     * 获取字符串属性 
     * @param key 
     * @return 
     */  
    private static String getPropString(String key) {  
        return prop == null ? null : prop.getProperty(key);  
    }  
  
    /** 
     * 重写 org.apache.ibatis.session.Configuration.StrictMap 类 
     * 来自 MyBatis3.4.0版本,修改 put 方法,允许反复 put更新。 
     */  
    public static class StrictMap extends HashMap {  
  
        private static final long serialVersionUID = -4950446264854982944L;  
        private String name;  
  
        public StrictMap(String name, int initialCapacity, float loadFactor) {  
            super(initialCapacity, loadFactor);  
            this.name = name;  
        }  
  
        public StrictMap(String name, int initialCapacity) {  
            super(initialCapacity);  
            this.name = name;  
        }  
  
        public StrictMap(String name) {  
            super();  
            this.name = name;  
        }  
  
        public StrictMap(String name, Map m) {  
            super(m);  
            this.name = name;  
        }  
  
        @SuppressWarnings("unchecked")  
        public V put(String key, V value) {  
            // ThinkGem 如果现在状态为刷新,则刷新(先删除后添加)  
            if (MapperRefresh.isRefresh()) {  
                remove(key);  
                MapperRefresh.log.debug("refresh key:" + key.substring(key.lastIndexOf(".") + 1));  
            }  
            // ThinkGem end  
            if (containsKey(key)) {  
                throw new IllegalArgumentException(name + " already contains value for " + key);  
            }  
            if (key.contains(".")) {  
                final String shortKey = getShortName(key);  
                if (super.get(shortKey) == null) {  
                    super.put(shortKey, value);  
                } else {  
                    super.put(shortKey, (V) new Ambiguity(shortKey));  
                }  
            }  
            return super.put(key, value);  
        }  
  
        public V get(Object key) {  
            V value = super.get(key);  
            if (value == null) {  
                throw new IllegalArgumentException(name + " does not contain value for " + key);  
            }  
            if (value instanceof Ambiguity) {  
                throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name  
                        + " (try using the full name including the namespace, or rename one of the entries)");  
            }  
            return value;  
        }  
  
        private String getShortName(String key) {  
            final String[] keyparts = key.split("\\.");  
            return keyparts[keyparts.length - 1];  
        }  
  
        protected static class Ambiguity {  
            private String subject;  
  
            public Ambiguity(String subject) {  
                this.subject = subject;  
            }  
  
            public String getSubject() {  
                return subject;  
            }  
        }  
    }  
}  

MyBatis有几个不太好的地方,是当实体类别名重名的时候,Mapper XML有错误的时候,系统启动时会一直等待无法正常启动(其实是加载失败后又重新加载,进入了死循环),这里重写下SqlSessionFactoryBean.java文件,解决这个问题,在这个文件里也加入启动上面写的线程类:

1、修改实体类重名的时候抛出并打印异常,否则系统会一直递归造成无法启动。
2、MapperXML有错误的时候抛出并打印异常,否则系统会一直递归造成无法启动。
3、加入启动MapperRefresh.java线程服务。

我下面会把SqlSessionFactoryBean.java所涉及到需要修改的地方会标注出来。这里童鞋们可能会问去哪里找SqlSessionFactoryBean.java这个文件,我在这里卡壳了,这里也给大家说一下,这个类是在mybatis-spring-1.2.2.jar这个包里面,是一个class文件。

这不是坑人吗,怎么改这个SqlSessionFactoryBean.class文件?我的具体的思路就是,先把这个class文件反编译成java文件,然后再去修改这个java文件,然后再把这个java文件编译成class文件,然后再放进mybatis-spring-1.2.2.jar包里替换掉之前的SqlSessionFactoryBean.class

我自己看着都绕,我来一步一步教大家吧
1、去找一个反编译软件,很多反编译软件,我这里用的是jd-gui.exe
下载地址:http://download.csdn.net/detail/eyebrows_cs/5277552

2、把下载好的jd-gui.exe打开,然后把mybatis-spring-1.2.2.jar拖到里面,找到SqlSessionFactoryBean.class这个文件,呈现在你面前的就是一个反编译过来的java文件了

SpringMVC+Mybatis+热部署mapper文件_第17张图片

3、修改这个java文件,然后再编译成class文件,这步我一开始没想明白怎么弄,最后用了一个笨方法。在你的项目下新建一个包:右键src > new Package

SpringMVC+Mybatis+热部署mapper文件_第18张图片

点击Finish,然后再包下面建一个类,名字就叫:SqlSessionFactoryBean

4、把下面这一大坨代码,复制到SqlSessionFactoryBean.java里面,下面这一大坨代码,我在改动的地方都加上了“修改处”三个字!!!

package org.mybatis.spring;

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.TypeAliasRegistry;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
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.event.ContextRefreshedEvent;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import com.alibaba.util.MapperRefresh;

public class SqlSessionFactoryBean
  implements FactoryBean, InitializingBean, ApplicationListener
{
  private static final Log logger = LogFactory.getLog(SqlSessionFactoryBean.class);
  private Resource configLocation;
  private Resource[] mapperLocations;
  private DataSource dataSource;
  private TransactionFactory transactionFactory;
  private Properties configurationProperties;
  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  private SqlSessionFactory sqlSessionFactory;
  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;
  private DatabaseIdProvider databaseIdProvider;
  private ObjectFactory objectFactory;
  private ObjectWrapperFactory objectWrapperFactory;

  public void setObjectFactory(ObjectFactory objectFactory)
  {
    this.objectFactory = objectFactory;
  }

  public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory)
  {
    this.objectWrapperFactory = objectWrapperFactory;
  }

  public DatabaseIdProvider getDatabaseIdProvider()
  {
    return this.databaseIdProvider;
  }

  public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider)
  {
    this.databaseIdProvider = databaseIdProvider;
  }

  public void setPlugins(Interceptor[] plugins)
  {
    this.plugins = plugins;
  }

  public void setTypeAliasesPackage(String typeAliasesPackage)
  {
    this.typeAliasesPackage = typeAliasesPackage;
  }

  public void setTypeAliasesSuperType(Class typeAliasesSuperType)
  {
    this.typeAliasesSuperType = typeAliasesSuperType;
  }

  public void setTypeHandlersPackage(String typeHandlersPackage)
  {
    this.typeHandlersPackage = typeHandlersPackage;
  }

  public void setTypeHandlers(TypeHandler[] typeHandlers)
  {
    this.typeHandlers = typeHandlers;
  }

  public void setTypeAliases(Class[] typeAliases)
  {
    this.typeAliases = typeAliases;
  }

  public void setFailFast(boolean failFast)
  {
    this.failFast = failFast;
  }

  public void setConfigLocation(Resource configLocation)
  {
    this.configLocation = configLocation;
  }

  public void setMapperLocations(Resource[] mapperLocations)
  {
    this.mapperLocations = mapperLocations;
  }

  public void setConfigurationProperties(Properties sqlSessionFactoryProperties)
  {
    this.configurationProperties = sqlSessionFactoryProperties;
  }

  public void setDataSource(DataSource dataSource)
  {
    if ((dataSource instanceof TransactionAwareDataSourceProxy))
    {
      this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();
    }
    else this.dataSource = dataSource;
  }

  public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder)
  {
    this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
  }

  public void setTransactionFactory(TransactionFactory transactionFactory)
  {
    this.transactionFactory = transactionFactory;
  }

  public void setEnvironment(String environment)
  {
    this.environment = environment;
  }

  public void afterPropertiesSet()
    throws Exception
  {
    Assert.notNull(this.dataSource, "Property 'dataSource' is required");
    Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

  protected SqlSessionFactory buildSqlSessionFactory()
    throws IOException
  {
    XMLConfigBuilder xmlConfigBuilder = null;
    Configuration configuration;
    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 (StringUtils.hasLength(this.typeAliasesPackage)) {
      String[] typeAliasPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; \t\n");

      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 (!ObjectUtils.isEmpty(this.typeAliases)) {
      for (Class typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        if (logger.isDebugEnabled()) {
          logger.debug("Registered type alias: '" + typeAlias + "'");
        }
      }
    }

    if (!ObjectUtils.isEmpty(this.plugins)) {
      for (Interceptor plugin : this.plugins) {
        configuration.addInterceptor(plugin);
        if (logger.isDebugEnabled()) {
          logger.debug("Registered plugin: '" + plugin + "'");
        }
      }
    }

    if (StringUtils.hasLength(this.typeHandlersPackage)) {
      String[] typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; \t\n");

      for (String packageToScan : typeHandlersPackageArray) {
        configuration.getTypeHandlerRegistry().register(packageToScan);
        if (logger.isDebugEnabled()) {
          logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
        }
      }
    }

    if (!ObjectUtils.isEmpty(this.typeHandlers)) {
      for (TypeHandler typeHandler : this.typeHandlers) {
        configuration.getTypeHandlerRegistry().register(typeHandler);
        if (logger.isDebugEnabled()) {
          logger.debug("Registered type handler: '" + typeHandler + "'");
        }
      }
    }

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();

        if (logger.isDebugEnabled())
          logger.debug("Parsed configuration file: '" + this.configLocation + "'");
      }
      catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }

    Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
    configuration.setEnvironment(environment);

    if (this.databaseIdProvider != null) {
      try {
        configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    if (!ObjectUtils.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);
  }

  public SqlSessionFactory getObject()
    throws Exception
  {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

  public Class getObjectType()
  {
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
  }

  public boolean isSingleton()
  {
    return true;
  }

  public void onApplicationEvent(ApplicationEvent event)
  {
    if ((this.failFast) && ((event instanceof ContextRefreshedEvent)))
    {
      this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
    }
  }
}

5、接下来我们就需要把这个SqlSessionFactoryBean.java文件编译成class文件,然后再复制到mybatis-spring-1.2.2.jar包里面
重新部署你的项目 Servers > Tomcat 7.x > 右键你的项目 Remove deployment 然后再 Add Deployment…你的项目

去Tomcat 7的根目录找到对应的SqlSessionFactoryBean.class文件复制出来
SpringMVC+Mybatis+热部署mapper文件_第19张图片

这里记得检查一下编译过的class文件是否正确,将你编译好的SqlSessionFactoryBean.class文件再次拖入jd-gui.exe,看看是不是和上面写的代码对应!!!!这步很重要,我之前编译成功之后,检查发现文件都不对了,记得必须检查一下

检查无误之后,把SqlSessionFactoryBean.class复制到mybatis-spring-1.2.2.jar(是你本地项目中的jar)包中,替换原来的class文件

6、一切准备就绪,还剩下最后一个属性文件, 创建mybatis-refresh.properties文件,记得把文件格式改成UTF-8
SpringMVC+Mybatis+热部署mapper文件_第20张图片

打开文件,把下面的内容复制进去

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

7、一切准备就绪,准备起飞火箭,这里需要注意,如果启动项目报错,那就把项目下咱们刚才新建的那个SqlSessionFactoryBean.java文件重命名一下,或者直接删除即可,它已经没有利用价值了

8、启动项目,然后随便修改一个mapper.xml文件,然后稍等片刻,在控制台出现如下输出,就表示你成功啦

SpringMVC+Mybatis+热部署mapper文件_第21张图片

这样就不用重启项目,也能加载到你修改的mapper.xml文件了

好了,一切都结束了,下班了,88童鞋们

你可能感兴趣的:(java,springMvc)