每天都是从网上找别人分享出来的好东西纳入囊中,今天自己也想分享一下自己的一些东西给大家。
一直在用SSH框架,没办法现在的主流就是SpringMvc+Mybatis,所以也研究了一下。看了几天网上分享的精髓,恕小弟我愚昧,搞了好久才弄明白其中的奥妙,不过我觉得自己懂了还不够深刻,必须拿出来和大家分享,让大家也能看懂才是真正的懂了。
我尽量写的简单点通俗易懂,只要是有点java基础就能看懂这篇文章是我的目标。
我们老大最近让了解markDown,写标题的时候正好看到有‘切换markDown编辑器’,直接开干,之前真的一点也不了解markDown。
(奥利给,我次有内,为给呀,老铁没毛病,今天又是一个楼面,100来平方,轻松拿下,干就完了!!)不要问我这句话啥意思,最近快手上有个雷哥,因这句口头禅火了。废话是不是有点太多了?好吧,我错了步入正题!!!
###我发现一个问题,也可能是我自己的问题,就是在看别人分享出来的资源的时候,总是跟着资源一步一步做,然后突然卡壳,要不就是报错要不就是各种纠结,瞬间继续看下去的心情就没了,这里建议大家耐心点,哪里卡壳了,就好好研究一下,当你迈过这个坎后,你肯定会收获更多
##开发工具
Myeclipse2014,jdk1.7,tomcat7,数据库我用的是oracle,伙计们这几个东西都先给我准备好了,马上开始打仗了。什么?这些东西你都不知道?用我们老家的口头禅来说“外日他dei儿,你给我去求吧”,这是作为一个程序员必须知道的,就像你打仗需要用枪一个道理!
##愤怒感言
每次跟着网上的资料学习,都会被一大堆的各种jar问题包搞的晕头转向,我总觉得这个是最浪费时间的,而且是能让人发疯的节奏!所以,这里我就先来拯救一下正在看我文章的小伙伴们吧,谁让你我最亲呢!我这里把所有jar包先贴出来,然后附上下载地址,请小伙伴们先不要问jar包的具体用途,先下载用了再说。
下载链接:http://download.csdn.net/detail/chunge48596/9709211
好了,小伙伴,我们离成功就差一步了,要不咱们先喝杯茶聊聊妹子聊聊人生吧。Set Fire to the Rain好听,你值得拥有
##项目结构
接下来我先把项目结构贴出来,有心的小伙伴可能会发现怎么看着有点眼熟?没办法,我也是从网上找的资源拼凑的,大家凑合着看吧。还想咋滴?不行咱们来打一架?哼~
卧槽,刚才手机振动了一下,打开一看居然发工资了,我这小心脏扑通扑通的,老板:给我来个鸡蛋灌饼,加2个蛋!!!!老子有钱
##开始盖楼
在Package Explorer空白处右键 > new > Web Project
next>next>各种next,最后Finish,然后出现如下结构
###然后咱们把上面讲到的jar包全部放进项目里,第一步打开项目家大门,第二部把jar包拿起来,第三部使劲把这些jar包扔进项目家里!!!
不逗你们啦,我是这样弄的,去Workspaces找到你刚建立好的项目,然后把所有jar包放进lib目录下,我的项目路径 > D:\Workspaces2014\spring-mvc\WebRoot\WEB-INF\lib
不知道自己的Workspaces项目路径在哪?我勒个擦,我的小哥,你确定你是个程序员吗?我再教教你,谁让我就是这么好呢!!!以后给我记住了,八嘎
右键你的项目名称 > Properties > Resource 然后你看到了什么?哦~ ~ ~ ~ ~知道了,在这里啊!
###下面就是数据库连接了,建立JDBC属性文件,项目都是这样,把数据库配置独立出来,这样是极好的
右键src > new > File,名字取名叫 jdbc.properties > Finish
右键 jdbc.properties > Properties
将 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盘,或者随便哪个盘里面创建一个文件夹,名字自取。文件夹里面的内容如下:
属实操蛋了,写完预览了一下,发现博客都看不清楚包的名字,我在列一下包名和文件名:
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
回车
这里要注意,如果执行报错,可能是因为 generatorConfig.xml 编码格式不对,用记事本打开generatorConfig.xml然后另存为ANSI格式,覆盖之前的generatorConfig.xml,再进行执行命令
然后你就可以去看看src文件夹里面的东西了,这里需要注意下生成的mapper文件,里面好像有点重复了,耐心点看看是否重复了,有重复就把重复的部分去掉即可
##接下来我们进行测试我们的代码是否能成功运行
将生成的文件复制到我们的项目里面,如下图:
不是说贴进来就行了,肯定会报错,把错误改改,就是一些包的位置和引入错误,改好之后需要注意一下mapper.xml文件,我接下来给伙伴们说一下mapper.xml文件各种关键字对应的位置和意思
ID, CATEGORY_LEVEL, PARENT_ID, CATEGORY_NAME, CATEGORY_EXPLAIN, PROFIT_MARGIN, CREATE_DATE,
UPDATE_DATE, REMARK
太费劲了,我只贴出来一部分代码,后面那些更新和删除就不贴出来了,就简单的给大家演示一下查询,我相信在我的调教下,这个查询会了之后,其他的比葫芦画瓢,你绝对能学会~
###接下来我们就可以进行测试了,创建test、service、serviceImpl三个文件,下面贴出来代码
###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,看控制台有没有输出你的数据,有数据,说明说明你已炼成葵花宝典第一式
##下面创建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方法返回的字符串要对应
<%@ 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下,然后运行项目
打开浏览器,输入:http://localhost:8080/spring-mvc
“点击查询行业信息”
#大功告成,艾玛,真累,到这里SpringMvc伙伴们就差不多学会了,我建议大家还是多看几遍过程,哪个地方不会去搜一下,或者你只需要一碗胡辣汤贿赂贿赂我,我就勉为其难的给你讲讲撒,哈哈,开个玩笑啦
#已经学会的同学可能会发现一个问题,就是每次我们修改一下mapper文件,就需要重启一下项目。
#项目小了还好,如果以后项目大了,修改一下mapper,重启一下项目,这尼玛蛋都碎一地了
#所以,又费老鼻子劲去找方法,最终功夫不负有心人,接下来,就给大家讲讲自动加载mapper文件的方法
#这个东西挺有意思,只要发现任何mapper文件被修改,就会重新加载被修改的mapper文件,**只加载被修改的mapper文件,只加载被修改的mapper文件,只加载被修改的mapper文件重要的事情说三遍!!!**网上还有些方法也能实现自动加载mapper文件,不过我觉得麻烦,而且每次修改一个mapper文件,就需要重新加载所有的mapper文件,效率上也不会太好,废话不多说,直接开始
##在src下创建util包,然后创建一个类,名字:MapperRefresh
然后把下面这一大坨代码贴进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文件了
3、修改这个java文件,然后再编译成class文件,这步我一开始没想明白怎么弄,最后用了一个笨方法。在你的项目下新建一个包:右键src > new Package
点击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 extends SqlSessionFactory> 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文件复制出来
这里记得检查一下编译过的class文件是否正确,将你编译好的SqlSessionFactoryBean.class文件再次拖入jd-gui.exe,看看是不是和上面写的代码对应!!!!这步很重要,我之前编译成功之后,检查发现文件都不对了,记得必须检查一下
检查无误之后,把SqlSessionFactoryBean.class复制到mybatis-spring-1.2.2.jar(是你本地项目中的jar)包中,替换原来的class文件
6、一切准备就绪,还剩下最后一个属性文件, 创建mybatis-refresh.properties文件,记得把文件格式改成UTF-8
打开文件,把下面的内容复制进去
#是否开启刷新线程
enabled=true
#延迟启动刷新程序的秒数
delaySeconds=60
#刷新扫描间隔的时长秒数
sleepSeconds=3
#扫描Mapper文件的资源路径
mappingPath=mapper
7、一切准备就绪,准备起飞火箭,这里需要注意,如果启动项目报错,那就把项目下咱们刚才新建的那个SqlSessionFactoryBean.java文件重命名一下,或者直接删除即可,它已经没有利用价值了,
8、启动项目,然后随便修改一个mapper.xml文件,然后稍等片刻,在控制台出现如下输出,就表示你成功啦
这样就不用重启项目,也能加载到你修改的mapper.xml文件了
好了,一切都结束了,下班了,88童鞋们