本文主要对SSM框架整合的过程进行记录,作为之后参考的依据。
Spring代码实例系列-绪论
Spring MVC代码实例系列-绪论
MyBatis代码实例系列-绪论
在整合的SSM框架中,主要涉及的框架、插件或技术有:
项目中只给出关键代码,如需项目的所有源代码,可以查看GitHub : https://github.com/hanchao5272/myssm。
在IDEA中:
File
–>New
–>Project...
–>选择Maven
项目
–>Project SDK
设置为1.8
–>勾选Create from archetype
–>选中maven-archetype-webapp
–>点击Next
–>录入GroupId
和ArtifactId
–>点击Next
–>点击Finish
–>等待IDEA完成项目初始化
–>完成项目创建
。
{project}\src\main\java
目录。{project}\src\test\java
目录。File
–>Project structure
–>Project Settings
–>Modules
中,将src\main\java
设置为Source Folders
,将src\test\java
设置为Test Source Folders
。{project}\.gitignore
文件,并编辑不需要上传至版本库的文件类型。{project}\README.md
文件,并编写项目的基本说明。Git Bash
通过git init
完成git
初始化。注意:这里没有配置jdk,是因为后面会通过编译插件maven-compiler-plugin
完成jdk配置。
这里直接列出完整的pom.xml,主要包括:
如过不需要某个插件或jar包,请自行移除。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>pers.hanchaogroupId>
<artifactId>myssmartifactId>
<packaging>warpackaging>
<version>1.0-SNAPSHOTversion>
<name>Spring + Spring MVC + MyBaits + Maven Examplename>
<url>http://maven.apache.orgurl>
<properties>
<compiler-plugin.version>3.7.0compiler-plugin.version>
<tomcat-plugin.version>2.2tomcat-plugin.version>
<commons-logging.version>1.2commons-logging.version>
<commons-lang3.version>3.3.2commons-lang3.version>
<commons-io.version>2.4commons-io.version>
<commons-fileupload.version>1.3.1commons-fileupload.version>
<log4j.version>1.2.17log4j.version>
<servlet-api.version>3.1.0servlet-api.version>
<jsp-api.version>2.0jsp-api.version>
<jstl.version>1.2jstl.version>
<springframework.version>5.0.0.RELEASEspringframework.version>
<jackson.version>2.9.0jackson.version>
<hibernate-validator.version>5.4.1.Finalhibernate-validator.version>
<mybatis.version>3.4.5mybatis.version>
<mybatis-spring.version>1.3.1mybatis-spring.version>
<mybatis-generator.version>1.3.2mybatis-generator.version>
<hairy.mybatis-generator.version>1.0.1hairy.mybatis-generator.version>
<pagehelper.version>5.1.2pagehelper.version>
<mysql.version>5.1.40mysql.version>
<junit.version>4.12junit.version>
properties>
<dependencies>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>${commons-lang3.version}version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>${commons-io.version}version>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>${commons-logging.version}version>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>${commons-fileupload.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>${servlet-api.version}version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>${jsp-api.version}version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>${jstl.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${springframework.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-oxmartifactId>
<version>${springframework.version}version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>${jackson.version}version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>${jackson.version}version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-annotationsartifactId>
<version>${jackson.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>${hibernate-validator.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>${mybatis.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>${mybatis-spring.version}version>
dependency>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>${mybatis-generator.version}version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>${pagehelper.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
<scope>testscope>
dependency>
dependencies>
<build>
<finalName>myssmfinalName>
<plugins>
plugins>
build>
project>
在pom.xml中配置编译插件maven-compiler-plugin
,如下:
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>${compiler-plugin.version}version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
plugins>
在pom.xml中配置tomcat插件tomcat7-maven-plugin
,如下:
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>${tomcat-plugin.version}version>
<configuration>
<path>/path>
<port>8080port>
<uriEncoding>utf-8uriEncoding>
configuration>
plugin>
plugins>
修改web.xml,进行web项目最初始配置:display-name和welcome-file-list,如下:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Spring + Spring MVC + MyBatis + Maven Web Applicationdisplay-name>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
web-app>
在IDEA中Edit Run/Debug configurations
界面,添加一个Maven
配置,修改Name
,并将Command line
设置为clean tomcat7:run -e
,然后保存退出。
通过点击或快捷键启动项目,在浏览器录入http://localhost:8080/,显示如下页面,证明项目初始化成功。
<log4j.version>1.2.17log4j.version>
编写main\resources\log4j.properties
,如下:
# 日志级别为DEBUG级别,设置两类日志信息存储媒介:控制台和文件
log4j.rootLogger = DEBUG,console,file
# 控制台日志配置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# 日志文件配置
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = himybatis.log
log4j.appender.file.datePattern = '.'yyyy-MM-dd
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.console.file.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
有关log4j的更详细配置,可以参考:Spring MVC代码实例系列-05:配置Log4j以及 log4j.properties 属性详解
创建pers.hanchao.myssm.test.log4j.Log4jDemo.java,如下
/**
* 测试log4j
* @author hanchao 2018/2/10 21:11
**/
public class Log4jDemo {
private static final Logger LOGGER = Logger.getLogger(Log4jDemo.class);
public static void main(String[] args) {
LOGGER.info("Hello World!");
}
}
运行,控制信息如下:
2018-02-10 21:14:01 INFO Log4jDemo:12 - Hello World!
这表明log4j配置正确。
<junit.version>4.12junit.version>
JUnitGenerator插件的安装可以参考: IDEA中使用Junit4进行测试的入门配置。
编写测试类JunitDemo.java
/**
* 测试junit
* @author hanchao 2018/2/5 22:03
**/
public class JunitDemo {
private static final Logger LOGGER = Logger.getLogger(JunitDemo.class);
/**
* 加法
* @author hanchao 2018/2/5 22:02
**/
public static int add(int a,int b){
LOGGER.info(a + b);
return a + b;
通过Alt+Insert
的快捷菜单生成junit4
单元测试类:JunitDemoTest.java,进行测试方法修改:
@Test
public void testAdd() throws Exception {
Assert.assertEquals(2,JunitDemo.add(1,1));
}
运行测试用例,显示1 test passed
,表名Junit4
可用。
本节记录如何配置Spring+Spring MVC。可供参考的文章如下:
Spring MVC代码实例系列-01:Spring MVC项目简单搭建与Hello Wolrd:较为详细的说明了Spring+SpringMVC整合及Spring MVC的工作原理。
<commons-logging.version>1.2commons-logging.version>
<commons-lang3.version>3.3.2commons-lang3.version>
<commons-io.version>2.4commons-io.version>
<servlet-api.version>3.1.0servlet-api.version>
<jsp-api.version>2.0jsp-api.version>
<jstl.version>1.2jstl.version>
<springframework.version>5.0.0.RELEASEspringframework.version>
<jackson.version>2.9.0jackson.version>
web.xml中关于Spring和Spring MVC的主要配置项:
此外,还有两个不必须但很重要的配置:
全部的web.xml配置如下:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Spring + Spring MVC + MyBatis + Maven Web Applicationdisplay-name>
<filter>
<filter-name>characterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>characterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<servlet>
<servlet-name>spring-mvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListenerlistener-class>
listener>
<servlet-mapping>
<servlet-name>spring-mvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
web-app>
spring-mvc-servlet.xml是Spring MVC的主要配置文件,在这里主要配置一下内容:
spring-mvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config/>
<mvc:annotation-driven/>
<context:component-scan base-package="pers.hanchao.myssm.*"/>
<mvc:resources mapping="/static/**" location="/static/" cache-period="31536000"/>
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="contentType" value="charset=utf-8"/>
<property name="order" value="10"/>
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
创建Spring的主配置文件applicationContext.xml,到这个阶段还是空配置,后续会增加配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
beans>
编写一个测试Controller,如下:
package pers.hanchao.myssm.test.spring;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
/**
* 测试Spring 和 Spring MVC的整合
* @author hanchao 2018/2/5 23:01
**/
@Controller
public class SpringMVCDemoController {
@GetMapping("/mvc/test")
public String mvcTest(Model model){
model.addAttribute("mvc","Spring + Spring MVC is OK!");
return "/mvc";
}
}
编写一个测试jsp,如下:
<%--
Created by IntelliJ IDEA.
User: hanchao
Date: 2018/2/5
Time: 23:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring + Spring MVC testtitle>
head>
<body>
Hi, ${mvc}
<br>
<a href="/index.jsp">返回首页a>
body>
html>
测试结果:
测试结果表明Spring和Spring MVC初步整合成功。
参考文章:
Spring MVC代码实例系列-12:使用自带的validation实现自定义message表单校验
Spring MVC代码实例系列-06:Spring MVC配置Hibernate-Validator以及自定义校验注解
这里使用的是Spring MVC代码实例系列-12所描述的配置方式,能够通过properties文件自定义message信息。
而且,可以通过EL表达式动态加载最大值、最小值之类的信息。
<hibernate-validator.version>5.4.1.Finalhibernate-validator.version>
在src/main/resources/目录下依次创建:
ValidationMessages.properties、ValidationMessages_zh_CN.properties。
在IDEA中,能够自动将这两个文件转换成一个Resources Bundle(国际化资源包),如下图所示:
ValidationMessages.properties:
# 全局配置/默认配置
user.username.length = 用户名 [${validatedValue}] 的长度必须在{min}至{max}之间!
user.password.length = 密码 [${validatedValue}] 的长度必须在{min}至{max}之间!
ValidationMessages_zh_CN.properties
# 中文(大陆)配置
user.username.length = 用户名 [${validatedValue}] 的长度必须在{min}至{max}之间!
user.password.length = 密码 [${validatedValue}] 的长度必须在{min}至{max}之间!
编写校验实体类User.java:
public class User {
/** 用户名 */
@NotNull
@Length(min = 5,max = 10, message = "{user.username.length}")
private String username;
/** 密码 */
@NotNull
@Length(min = 5,max = 10, message= "{user.password.length}")
private String password;
//setter getter toString equals&hashCode
}
编写JsonResult.java,以泛型方式装载返回对象
public class JsonResult<E> {
/** 响应状态 */
private Integer code = 1;
/** 响应消息 */
private String message = "success!";
/** 响应内容 */
private List list;
//setter getter toString equals&hashCode
}
编写ValidationUtils,统一处理校验失败信息:
public class ValidationUtils {
/**
* 进行校验,如果有错误,将结果赋给jsonResult,并返回false;如果无错误,则返回true
* @author hanchao 2018/2/7 23:33
**/
public static boolean validateAndSetJsonResult(BindingResult bindingResult, JsonResult jsonResult){
if (bindingResult.hasErrors()){
StringBuffer errors = new StringBuffer();
List allErrors = bindingResult.getAllErrors();
for (ObjectError objectError : allErrors){
errors.append(objectError.getDefaultMessage() + "\n");
}
jsonResult.setCode(-1);
jsonResult.setMessage(errors.toString());
return false;
}else {
return true;
}
}
}
修改SpringMVCDemoController,编写表单校验测试方法:
/**
* 简单测试注解、校验和JsonResult
* @author hanchao 2018/2/7 23:27
**/
@PostMapping("/mvc/login")
@ResponseBody
public JsonResult loginTest(@Valid @RequestBody User user, BindingResult bindingResult){
LOGGER.info(user.toString());
//定义返回值
JsonResult jsonResult = new JsonResult();
//表单校验
if (ValidationUtils.validateAndSetJsonResult(bindingResult,jsonResult)){//如果没错误
List list = new ArrayList();
list.add(user);
jsonResult.setList(list);
}
//返回结果
return jsonResult;
}
在这里用到了@PostMapping、@ResponseBody、@RequestBody等注解,参考文件:
Spring MVC代码实例系列-03:@PathVariable、@RequestHeader、@RequestParam、@RequestBody等
编写校验测试页面login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录测试:注解、校验title>
head>
<script type="text/javascript" src="../static/js/jquery-3.2.1.min.js">script>
<body>
<div>
<input type="text" value="David" name="username" id="username"/>
<input type="password" value="123456" name="password" id="password"/>
<input type="button" value="校验测试" onclick="login()"/>
div>
body>
<script type="text/javascript">
function login() {
$.ajax({
type:'POST',
url:'/mvc/login',
data:JSON.stringify({username:$('#username').val(),password:$('#password').val()}),
contentType:'application/json;charset=utf-8',
success:function (data) {
alert(data.message);
},
error:function (data) {
alert(data.message);
}
});
}
script>
html>
拦截器是Spring AOP(Aspected-Oriented Programming,面向切面的编程)的一种实现。
拦截器的应用场景有很多,如:日志记录、权限检查、性能监控、通用行为等等。
这里只给出一个简单的方法执行时间拦截器作为测试。如果想了解更多内容,可以参考:
Spring MVC代码实例系列-11:Spring MVC实现简单的权限控制拦截器和请求信息统计拦截器
编写MethodTimeInterceptor,计算每个方法的执行时间:
package pers.hanchao.myssm.test.interceptor;
import org.apache.log4j.Logger;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Clock;
/**
* 简单的方法执行时间拦截器,用来验证mvc拦截器可用
* Created by 韩超 on 2018/2/8.
*/
public class MethodTimeInterceptor extends HandlerInterceptorAdapter{
private final static Logger LOGGER = Logger.getLogger(MethodTimeInterceptor.class);
private ThreadLocal startTime = new ThreadLocal();
/**
* Title: 方法执行前记录时间
* @author 韩超@bksx 2018/2/8 10:20
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果是方法处理
if (handler instanceof HandlerMethod){
//Java 8 Clock
startTime.set(Clock.systemDefaultZone().millis());
}
return true;
}
/**
* Title: 方法执行完成计算时间
* @author 韩超@bksx 2018/2/8 10:20
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
//如果是方法处理
if (handler instanceof HandlerMethod){
//获取方法处理时间
Long useTime = Clock.systemDefaultZone().millis() - startTime.get();
StringBuffer sb = new StringBuffer();
//设置访问URI
sb.append("[URI = ").append(request.getRequestURI());
//设置请求类型
sb.append(", type = ").append(request.getMethod());
//设置用时
sb.append(", useTime = ").append(useTime).append("ms]");
LOGGER.info(sb.toString());
}
}
}
修改spring-mvc-serlvet.xml,通过mvc:interceptor标签配置MehtodTimeInterceptor拦截器,如下:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="pers.hanchao.myssm.test.interceptor.MethodTimeInterceptor"/>
mvc:interceptor>
mvc:interceptors>
任意运行一个请求,如登录,查看控制台日志如下:
2018-02-10 22:16:04 INFO MethodTimeInterceptor:49 - [URI = /mvc/login, type = POST, useTime = 297ms]
表明拦截器配置成功。其他更高级的拦截器都按照这个模式进行配置即可。
这里只给出简单的文件上传配置,参考文章:
Spring MVC代码实例系列-10:Spring MVC实现简单的文件上传和下载
<commons-fileupload.version>1.3.1commons-fileupload.version>
修改spring-mvc-servlet.xml添加CommonsMultpartResolver,尤其注意:
- maxUploadSize:一次上传文件的最大byte值.
- maxUploadSizePerFile:一个上传文件的最大byte值.
配置如下:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
<property name="maxUploadSizePerFile" value="1048576"/>
<property name="defaultEncoding" value="UTF-8"/>
bean>
编写上传文件控制器:
/**
* 简单的文件上传:用于展示MultiPartFile
* Created by 韩超 on 2018/2/8.
*/
@Component
@RequestMapping("file")
public class FileUtilsDemo {
private final static Logger LOGGER = Logger.getLogger(FileUtilsDemo.class);
/**
* Title: 文件上传
* @author 韩超@bksx 2018/2/8 11:12
*/
@PostMapping("/upload")
public String upload(HttpServletRequest request, @RequestBody MultipartFile[] multiFiles,Model model){
//获取上传根目录
String uploadRootPath = request.getServletContext().getRealPath("upload");
LOGGER.info("当前上传文件根路径:" + uploadRootPath);
File rootFile = new File(uploadRootPath);
//判断目录是否存在,不存在层级目录
if (!rootFile.exists()){
rootFile.mkdirs();
}
//设置返回值,默认为成功
JsonResult jsonResult = new JsonResult();
//上传目录
try {
for (MultipartFile file : multiFiles){
//如果文件不为空,则可以上传
if (null != file){
File serverFile = new File(rootFile.getAbsolutePath() + File.separator + file.getOriginalFilename());
LOGGER.info("当前上传文件路径:" + serverFile.getAbsolutePath());
if (serverFile.exists()){
serverFile.delete();
}
file.transferTo(serverFile);
}
}
} catch (IOException e) {
e.printStackTrace();
LOGGER.info("文件上传失败!");
//设置返回消息为失败
jsonResult.setCode(-1);
jsonResult.setMessage("文件上传失败!");
}
model.addAttribute("result",jsonResult);
return "test/file";
}
}
编写测试jsp页面,尤其注意:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>简单文件上传示例title>
head>
<body>
<form method="post" action="/file/upload" enctype="multipart/form-data">
<input type="file" name="multiFiles"/><br/>
<input type="file" name="multiFiles"/><br/>
<input type="file" name="multiFiles"/><br/>
<input type="submit" value="上传"/>
form>
<div>
<h3>code:${code},message:${message}h3>
div>
body>
html>
参考文章:
MyBatis代码实例系列-05:Mybatis初步整合Spring + Spring MVC框架,实现Web请求实例
通过修改applicationContext.xml,引入(import)Spring-Mybatis的配置文件:
<import resource="spring-mybatis.xml"/>
spring-mybatis.xml依赖于mybatis-spring-x.y.z.jar,能够帮助我们将MyBatis大部分配置无缝地整合到 Spring 中。spring-mybatis.xml的主要配置:
详细配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder file-encoding="UTF-8" location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mybatis-mapper/*.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="pers.hanchao.myssm.**.dao"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
beans>
关于context:property-placeholder
的作用,可以参考:
Spring代码实例系列-09:通过Spring PropertyPlaceholderConfigurer将properties配置的属性注入到xml配置文件中
创建jdbc.properties,用于配置数据库连接信息:
# 数据库连接信息
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/exam?useSSL=false
jdbc.username=root
jdbc.password=******
创建MyBatis的主配置文件mybatis-config.xml,用于配置一些Mybatis的专有配置(目前为空配置):
<configuration>
configuration>
SQL语句:
drop table topic;
create table `topic`(
`id` int(5) unsigned not null auto_increment comment '话题',
`title` varchar(20) not null comment '题目',
`score` int(3) not null comment '分数',
`answer` varchar(100) comment '答案',
primary key(id)
)engine=InnoDB comment='话题' auto_increment=1 default charset=utf8;
insert into topic values(99999,'题目1',100,'你好吗?');
目录结构:
src\main\java\
| pers.hanchao.myssm.test.curd
| \---controller
| \---TopicController.java
| \---dao
| \---TopicDao.java
| \---entity
| \---TopicEntity.java
| \---service
| \---TopicService.java
| |---impl
| \---TopicServiceImpl
src\main\resources\
mybatis-mapper
\---TopicDao.xml
TopicController.java
控制层,通过@Autowired自动装配接口TopicService,符合设计模式中的面向接口编程。
/**
* 简单实例测试Mybatis的事务控制和增删改查
* @author hanchao 2018/2/8 23:55
**/
@Controller
public class TopicController {
@Autowired
private TopicService topicService;
/**
* 简单测试增删改查
* @author hanchao 2018/2/8 23:41
**/
@GetMapping("mybatis/curd")
public String curdTest(){
this.topicService.curdTest();
return "test/curd";
}
}
TopicService.java
服务接口层,注意:在接口中通过注解@Transactional进行声明式事务控制。
public interface TopicService {
@Transactional
void curdTest();
}
TopicServiceImpl.java
服务实现层,注意:通过@Autowired自动装配的接口TopicDao并没有显示实现。
因为在TopicService.java已经进行了事务控制,这里不需要再进行注解。
@Service
public class TopicServiceImpl implements TopicService {
private static final Logger LOGGER = Logger.getLogger(TopicServiceImpl.class);
@Autowired
private TopicDao topicDao;
/**
* 简单测试事务注解和增删改查
* @author hanchao 2018/2/8 23:45
**/
@Override
public void curdTest() {
//查询所有
LOGGER.info("查询所有");
LOGGER.info(this.topicDao.selectAll());
//插入一个,查询所有
System.out.println();
LOGGER.info("插入一个");
TopicEntity newTopic = new TopicEntity();
newTopic.setId(100);
newTopic.setTitle("新增的标题");
newTopic.setScore(200);
newTopic.setAnswer("新增的答案");
this.topicDao.insert(newTopic);
LOGGER.info(this.topicDao.selectAll());
//查询一个
System.out.println();
LOGGER.info("查询一个");
LOGGER.info(this.topicDao.selectByPrimaryKey(99999));
//修改一个,然后查询
System.out.println();
LOGGER.info("修改一个");
newTopic = new TopicEntity();
newTopic.setId(1);
newTopic.setTitle("修改的标题");
newTopic.setScore(1000);
newTopic.setAnswer("修改的答案");
this.topicDao.updateByPrimaryKey(newTopic);
LOGGER.info(this.topicDao.selectByPrimaryKey(99999));
//删除一个,然后查询
System.out.println();
LOGGER.info("删除一个,然后查询");
this.topicDao.deleteByPrimaryKey(99999);
LOGGER.info(this.topicDao.selectByPrimaryKey(99999));
}
}
TopicDao.java
/**
* 话题Dao
* @author hanchao 2018/2/8 23:39
**/
@Repository
public interface TopicDao {
/**
* 根据主键删除数据库的记录
*
* @param id
*/
int deleteByPrimaryKey(Integer id);
/**
* 插入数据库记录
*
* @param record
*/
int insert(TopicEntity record);
/**
* 根据主键获取一条数据库记录
*
* @param id
*/
TopicEntity selectByPrimaryKey(Integer id);
/**
* 获取全部数据库记录
*
*/
List selectAll();
/**
* 根据主键来更新数据库记录
*
* @param record
*/
int updateByPrimaryKey(TopicEntity record);
}
TopicEntity.java:
/**
* 话题
* @author hanchao 2018/2/8 23:33
**/
public class TopicEntity {
/**
*
* 话题
* 表字段 : topic.id
*
*/
private Integer id;
//other fields
// setter getter toString equals&hashCode
}
TopicDao.xml
好吧,我承认这里偷懒了,这里是用MBG(后面会讲)生成的代码。
<mapper namespace="pers.hanchao.myssm.test.curd.dao.TopicDao">
<resultMap id="BaseResultMap" type="pers.hanchao.myssm.test.curd.entity.TopicEntity">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="title" jdbcType="VARCHAR" property="title" />
<result column="score" jdbcType="INTEGER" property="score" />
<result column="answer" jdbcType="VARCHAR" property="answer" />
resultMap>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from topic
where id = #{id,jdbcType=INTEGER}
delete>
mapper>
测试结果
2018-02-10 22:54:01 INFO TopicServiceImpl:32 - 查询所有
2018-02-10 22:54:01 INFO TopicServiceImpl:33 - [TopicEntity [Hash = 1936880702, id=99999, title=题目1, score=100, answer=你好吗?], TopicEntity [Hash = -1744284694, id=100000, title=新增的标题, score=200, answer=新增的答案], TopicEntity [Hash = -1744254903, id=100001, title=新增的标题, score=200, answer=新增的答案], TopicEntity [Hash = -1744225112, id=100002, title=新增的标题, score=200, answer=新增的答案]]
2018-02-10 22:54:01 INFO TopicServiceImpl:45 - 插入一个
2018-02-10 22:54:01 INFO TopicServiceImpl:52 - [TopicEntity [Hash = 1936880702, id=99999, title=题目1, score=100, answer=你好吗?], TopicEntity [Hash = -1744284694, id=100000, title=新增的标题, score=200, answer=新增的答案], TopicEntity [Hash = -1744254903, id=100001, title=新增的标题, score=200, answer=新增的答案], TopicEntity [Hash = -1744225112, id=100002, title=新增的标题, score=200, answer=新增的答案], TopicEntity [Hash = -1744195321, id=100003, title=新增的标题, score=200, answer=新增的答案]]
2018-02-10 22:54:01 INFO TopicServiceImpl:55 - 查询一个
2018-02-10 22:54:01 INFO TopicServiceImpl:56 - TopicEntity [Hash = 1936880702, id=99999, title=题目1, score=100, answer=你好吗?]
2018-02-10 22:54:01 INFO TopicServiceImpl:59 - 修改一个
2018-02-10 22:54:01 INFO TopicServiceImpl:66 - TopicEntity [Hash = 1936880702, id=99999, title=题目1, score=100, answer=你好吗?]
2018-02-10 22:54:01 INFO TopicServiceImpl:69 - 删除一个,然后查询
2018-02-10 22:54:01 INFO TopicServiceImpl:71 -
2018-02-10 22:54:02 INFO MethodTimeInterceptor:49 - [URI = /mybatis/curd, type = GET, useTime = 1100ms]
测试结果,证明MyBatis整合成功。
参考文章:
MyBatis代码实例系列-02:MyBatis用log4j打印SQL以及MyBatis的事务控制
MyBatis代码实例系列-09:初步整合Spring + Spring MVC框架之后,如何打印MyBatis的SQL语句
实现对MyBatis对SQL的打印在我们开发调试中十分有用,下面对其配置方法进行说明:
将log4j的日志级别设置为debug或更低级别
log4j.rootLogger = debug,console,file
在MyBatis配置文件中添加setting配置:name="logImpl" value="LOG4J"
,指定使用log4j作为日志实现:
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
configuration>
2018-02-10 23:14:44 INFO TopicServiceImpl:55 - 查询一个
2018-02-10 23:14:44 DEBUG SqlSessionUtils:97 - Creating a new SqlSession
2018-02-10 23:14:44 DEBUG SqlSessionUtils:148 - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@10e54129] was not registered for synchronization because synchronization is not active
2018-02-10 23:14:44 DEBUG DataSourceUtils:114 - Fetching JDBC Connection from DataSource
2018-02-10 23:14:44 DEBUG DriverManagerDataSource:143 - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/exam?useSSL=false]
2018-02-10 23:14:44 DEBUG SpringManagedTransaction:87 - JDBC Connection [com.mysql.jdbc.JDBC4Connection@294e531c] will not be managed by Spring
2018-02-10 23:14:44 DEBUG selectByPrimaryKey:159 - ==> Preparing: select id, title, score, answer from topic where id = ?
2018-02-10 23:14:44 DEBUG selectByPrimaryKey:159 - ==> Parameters: 99999(Integer)
2018-02-10 23:14:44 DEBUG selectByPrimaryKey:159 - <== Total: 0
2018-02-10 23:14:44 DEBUG SqlSessionUtils:191 - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@10e54129]
2018-02-10 23:14:44 DEBUG DataSourceUtils:340 - Returning JDBC Connection to DataSource
2018-02-10 23:14:44 INFO TopicServiceImpl:56 -
测试结果证明配置成功。
参考文章:
MyBatis代码实例系列-10:MyBatis通过PageHelper插件实现分页查询
这里使用的一款开源的MyBatis分页插件:com.github.pagehelper。
GItHub地址:https://github.com/pagehelper/Mybatis-PageHelper
<pagehelper.version>5.1.2pagehelper.version>
MyBatis配置文件,添加pagehelper拦截器:
<configuration>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="pageSizeZero" value="true"/>
<property name="reasonable" value="true"/>
plugin>
plugins>
configuration>
在业务代码中:
在之前的TopicServiceImpl.java
中,进行修改:
//查询所有
LOGGER.info("查询所有");
LOGGER.info(this.topicDao.selectAll());
//查询所有(分页)
System.out.println();
PageHelper.startPage(0,10);
LOGGER.info("查询所有(分页)");
//将Page类型转换成List
List topicEntityList = topicDao.selectAll();
LOGGER.info(topicEntityList);
//通过lambda表达式输出集合信息
topicEntityList.forEach(LOGGER::info);
再次运行,控制台信息:
2018-02-10 22:54:01 INFO TopicServiceImpl:32 - 查询所有
2018-02-10 22:54:01 INFO TopicServiceImpl:33 - [TopicEntity [Hash = 1936880702, id=99999, title=题目1, score=100, answer=你好吗?], TopicEntity [Hash = -1744284694, id=100000, title=新增的标题, score=200, answer=新增的答案], TopicEntity [Hash = -1744254903, id=100001, title=新增的标题, score=200, answer=新增的答案], TopicEntity [Hash = -1744225112, id=100002, title=新增的标题, score=200, answer=新增的答案]]
2018-02-10 22:54:01 INFO TopicServiceImpl:37 - 查询所有(分页)
2018-02-10 22:54:01 INFO TopicServiceImpl:40 - Page{count=true, pageNum=1, pageSize=10, startRow=0, endRow=10, total=4, pages=1, reasonable=true, pageSizeZero=true}
2018-02-10 22:54:01 INFO TopicServiceImpl:1249 - TopicEntity [Hash = 1936880702, id=99999, title=题目1, score=100, answer=你好吗?]
2018-02-10 22:54:01 INFO TopicServiceImpl:1249 - TopicEntity [Hash = -1744284694, id=100000, title=新增的标题, score=200, answer=新增的答案]
2018-02-10 22:54:01 INFO TopicServiceImpl:1249 - TopicEntity [Hash = -1744254903, id=100001, title=新增的标题, score=200, answer=新增的答案]
2018-02-10 22:54:01 INFO TopicServiceImpl:1249 - TopicEntity [Hash = -1744225112, id=100002, title=新增的标题, score=200, answer=新增的答案]
经验证,分页插件可用。
参考文章:
MyBatis代码实例系列-08:MyBatisGenerator插件及扩展(中文注释和Mapper重命名为Dao)
在使用MyBatis框架进行开发时,每用到一张表就要手动创建实体类、XML映射文件和映射接口,这些都是重复性工作,耗费了我们大量精力。
MyBatis Generator (简称MBG) 是一个Mybatis的代码生成器。它可以根据数据库中的表,生成对应的实体类、XML映射文件和映射接口,节约了大量的时间去开发和业务逻辑有关的功能。
当然,如果是多表联合查询和存储过程,仍然需要手动处理。
git clone
git clone git@github.com:li24361/mybatis-generator-core.git
mvn install
mvn install -Dmaven.test.skip=true
<mybatis-generator.version>1.3.2mybatis-generator.version>
<hairy.mybatis-generator.version>1.0.1hairy.mybatis-generator.version>
说明:
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>${mybatis-generator.version}version>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>com.haier.hairygroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>${hairy.mybatis-generator.version}version>
dependency>
dependencies>
<configuration>
<configurationFile>src/main/resources/generatorConfig.xmlconfigurationFile>
<overwrite>trueoverwrite>
<verbose>trueverbose>
configuration>
plugin>
修改MBG的配置文件:generatorConfig.xml。主要配置项:
详细配置:
<generatorConfiguration>
<properties resource="jdbc.properties"/>
<context id="MysqlContext" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="${mgb.table.delimiter}"/>
<property name="endingDelimiter" value="${mgb.table.delimiter}"/>
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>
<commentGenerator type="org.mybatis.generator.internal.HairyCommentGenerator">
<property name="javaFileEncoding" value="UTF-8"/>
<property name="suppressAllComments" value="${mgb.comment.suppress.all}"/>
<property name="suppressDate" value="${mgb.comment.suppress.date}"/>
commentGenerator>
<jdbcConnection driverClass="${jdbc.driverClassName}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}">
jdbcConnection>
<javaModelGenerator targetPackage="${mgb.entity.package}" targetProject="${mgb.entity.project}">
<property name="enableSubPackages" value="${mgb.enableSubPackages}"/>
<property name="trimStrings" value="true"/>
javaModelGenerator>
<sqlMapGenerator targetPackage="${mgb.xml.package}" targetProject="${mgb.xml.project}">
<property name="enableSubPackages" value="${mgb.enableSubPackages}"/>
sqlMapGenerator>
<javaClientGenerator targetPackage="${mgb.dao.package}" targetProject="${mgb.dao.project}" type="XMLMAPPER">
<property name="enableSubPackages" value="${mgb.enableSubPackages}"/>
javaClientGenerator>
<table schema="exam" tableName="apple">
<generatedKey column="id" sqlStatement="MySql"/>
table>
context>
generatorConfiguration>
修改jdbc.properties,添加MBG相关配置:
# MyBatis Generator配置信息
## 表名分隔符
mgb.table.delimiter=`
## 注释抑制配置
mgb.comment.suppress.all=false
mgb.comment.suppress.date=true
## 是否运行根据schema创建子包
mgb.enableSubPackages=true
## entity配置
mgb.entity.project=src\\main\\java
mgb.entity.package=pers.hanchao.myssm.generator.entity
## Dao配置
mgb.dao.project=src\\main\\java
mgb.dao.package=pers.hanchao.myssm.generator.dao
## XML配置
mgb.xml.project=src\\main\\resources
mgb.xml.package=generator
构建命令:
mybatis-generator:generate
构建结果:
src\main\java\
| pers.hanchao.myssm.test.curd
| \---dao
| \---AppleDao.java
| \---entity
| \---AppleEntity.java
src\main\resources\
generator
\---AppleDao.xml
测试成功!
全部整合过程:
其中,