SSM之SpringMVC和Mybatis
一、文章介绍
- SpringMVC
- Mybatis
- SSM整合
二、SpringMVC
(一)简介
-
概念
SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架。它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
-
快速入门
需求:客户端发起请求,服务端接收请求,执行逻辑并进行视图跳转。
开发步骤:
-
导入SpringMVC相关坐标
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.8</version> </dependency> </dependencies>
-
web.xml中配置SpringMVC核心控制器DispathcerServlet
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
创建Controller类和视图页面
-
使用注解配置Controller类中业务方法的映射地址
@Controller public class HeroController { @RequestMapping("/hello") public String hello(){ System.out.println("hello···"); return "hello.jsp"; } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 你好,SpringMVC! </body> </html>
-
配置SpringMVC核心文件spring-mvc.xml
<context:component-scan base-package="com.cqgcxy.controller"></context:component-scan>
-
配置Tomcat并启动
-
客户端发起请求测试
-
-
执行流程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器。
- 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter处理器适配器。
- HandlerAdapter经过适配调用具体的Handler处理器(Controller,也叫后端控制器)。Controller执行完成返回ModelAndView。
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
- ViewReslover解析后返回具体View视图。
- DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
- DispatcherServlet响应用户。
(二)组件解析
-
RequestMapping
- 作用:用于建立请求URL和处理请求方法之间的对应关系
- 位置:
- 类上,请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录
- 方法上,请求URL的第二级访问目录,与类上的使用@RequestMapping标注的一级目录一起组成访问虚拟路径
- 属性:
- value:用于指定请求的URL。它和path属性的作用是一样的
- method:用于指定请求的方式
- params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样
- 例如:
- params = {“accountName”},表示请求参数必须有accountName
- params = {“moeny!100”},表示请求参数中money不能是100
- 例如:
@Controller @RequestMapping("/hero") public class HeroController { @RequestMapping(value = "/hello",method = RequestMethod.GET,params = {"heroId"}) public String hello(){ System.out.println("hello···"); return "hello.jsp"; } }
(三)数据响应
-
页面跳转
-
视图解析器
SpringMVC有默认组件配置,默认组件都是Dispatcherservlet.properties配置文件中配置的,该配置文件地址
org/springframework/web/ servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceviewResolver
翻看该解析器源码,可以看到该解析器的默认设置,如下:
public static final String REDIRECT_URL_PREFIX = "redirect:"; //重定向前缀 public static final String FORWARD_URL_PREFIX = "forward:"; //请求转发前缀 private String prefix = ""; //视图名称前缀 private String suffix = ""; //视图名称后缀
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean>
@Controller @RequestMapping("/hero") public class HeroController { @RequestMapping("/hello") public String hello(){ System.out.println("hello···"); return "hello"; } }
-
返回字符串形式
-
**直接返回字符串:**此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转
资源地址:/WEB-INF/views/hello.jsp
-
返回带有前缀的字符串:
转发:forward:/WEB-INF/views/hello.jsp
重定向:redirect:/index.jsp
@RequestMapping("/forward") public String forword(){ System.out.println("forward···"); return "forward:/WEB-INF/views/index.jsp"; } @RequestMapping("/redirect") public String redirect(){ System.out.println("redirect···"); return "redirect:/login.jsp"; }
-
-
返回ModelAndView对象
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 你好,SpringMVC!${username} </body> </html>
@RequestMapping("/hello2") public ModelAndView hello2(){ //Model:模型,用于封装数据 //View:视图,用于展示数据 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("username","pep7chiao"); modelAndView.setViewName("hello"); return modelAndView; } @RequestMapping("/hello3") public ModelAndView hello3(ModelAndView modelAndView){ modelAndView.addObject("username","pep7chiao"); modelAndView.setViewName("hello"); return modelAndView; } @RequestMapping("/hello4") public String hello4(Model model){ model.addAttribute("username","messi"); return "hello"; } @RequestMapping("/hello5") public String hello5(HttpServletRequest reqest){ //HttpServletRequest需要添加依赖 reqest.setAttribute("username","ronaldo"); return "hello"; }
-
-
回写数据
-
直接返回字符串
Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print(“hello world”))即可,那么在Controller中想直接回写字符串该怎样呢?
- 通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”)回写数
据,此时不需要视图跳转,业务方法返回值为void。
@RequestMapping("/data1") public void data1(HttpServletResponse response) throws IOException { response.setContentType("text/html;charset=utf-8"); response.getWriter().print("重庆工程学院"); }
-
将需要回写的字符串直接返回,但此时需要通过**@ResponseBody**注解告知SpringMVC框架,方法返回的字符串不是跳
转,而是直接在http响应体中返回。
@RequestMapping(value = "/data2",produces = "text/html;charset=utf-8") @ResponseBody public String data2(){ return "软件工程研究所"; }
-
返回对象或集合
- 导入json相关依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
- 通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> </bean> </list> </property> </bean>
@RequestMapping("/data3") @ResponseBody public Hero data3() throws IOException { Hero hero = new Hero(); hero.setId(1L); hero.setHeroName("李白"); return hero; }
- 在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,因此,我们可以使用mvc的注解驱动代替上述配置。
<mvc:annotation-driven/>
- 在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping (处理映射器)和
RequestMappingHandlerAdapter(处理适配器),可用在Springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。 - 同时使用<mvc:annotation-driven>默认底层就会集成jackson进行对象或集合的json格式字符串的转换。
-
(四)请求
客户端请求参数的格式是:name=value&name=value…
服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:
- 基本类型参数
- POJO类型参数
- 数组类型参数
- 集合类型参数
-
获取基本类型参数
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/hero/param1?id=1&heroName=李白
@RequestMapping("/param1") @ResponseBody public void param1(int id,String heroName){ System.out.println(id); System.out.println(heroName); }
当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。
http://localhost:8888/hero/param1?id=1&hname=李白
@RequestMapping("/param1") @ResponseBody public void param1(int id,@RequestParam("hname") String heroName){ System.out.println(id); System.out.println(heroName); }
注解@RequestParam还有如下参数可以使用:
- value::请求参数名称
- required::指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
- defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
http://localhost:8888/hero/param1?id=1
@RequestMapping("/param1") @ResponseBody public void param1(int id,@RequestParam(value = "hname",required = false,defaultValue = "李白") String heroName){ System.out.println(id); System.out.println(heroName); }
-
POJO类型参数
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/hero/param1?id=1&heroName=李白
@RequestMapping("/param2") @ResponseBody public void param2(Hero hero){ System.out.println(hero.getId()); System.out.println(hero.getHeroName()); }
-
数组类型参数
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/hero/param3?roles=诗人&roles=刺客
@RequestMapping("/param3") @ResponseBody public void param3(String[] roles){ System.out.println(Arrays.asList(roles)); } @RequestMapping("/param3") @ResponseBody public void param3(@RequestParam List<String> roles){ System.out.println(roles); }
-
集合类型参数
获取集合参数时,要将集合参数包装到一个POJO中才可以。
public class VO { private List<Hero> heroList; public List<Hero> getHeroList() { return heroList; } public void setHeroList(List<Hero> heroList) { this.heroList = heroList; } @Override public String toString() { return "VO{" + "heroList=" + heroList + '}'; } }
<form action="${pageContext.request.contextPath}/hero/param4" method="post"> <p> 编号1:<input type="text" name="heroList[0].id"> </p> <p> 名字1:<input type="text" name="heroList[0].heroName"> </p> <p> 编号2:<input type="text" name="heroList[1].id"> </p> <p> 名字2:<input type="text" name="heroList[1].heroName"> </p> <p> <input type="submit" value="提交"> </p> </form>
@RequestMapping(value = "/param4",method = RequestMethod.POST) @ResponseBody public void param4(VO vo){ System.out.println(vo); }
乱码问题:当表单提交post请求时,数据会出现乱码,可以设置一个过滤器来进行编码的过滤。
<filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <servlet-name>dispatcherServlet</servlet-name> </filter-mapping>
当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。
<script src="/js/jquery-3.3.1.min.js" type="text/javascript"></script> <script> var heroList=new Array(); heroList.push({id:1,heroName:"李白"}); heroList.push({id:2,heroName:"杜甫"}); console.info(JSON.stringify(heroList)) $.ajax({ url:"${pageContext.request.contextPath}/hero/param5", type:"post", data:JSON.stringify(heroList), contentType:"application/json;charset=utf-8" }) </script>
@RequestMapping(value = "/param5",method = RequestMethod.POST) @ResponseBody public void param5(@RequestBody List<Hero> heroList){ System.out.println(heroList); }
注意:这时会发现找不到js文件,需要开放资源访问。
<!-- 方式1:开放资源访问--> <mvc:resources mapping="/js/**" location="/js/"></mvc:resources> <!-- 方式2:找不到对应资源时,交给tomcat容器去找具体路径--> <mvc:default-servlet-handler/>
-
获得Restful风格的参数
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP协议里面四个表示操作方式的动词如下:
-
GET:用于获取资源
-
POST:用于新建资源
-
PUT:用于更新资源
-
DELETE:用于删除资源
例如:
-
/user/1 GET :得到id =1的user
-
/user/1 DELETE:删除id = 1的user
-
/user/1 PUT:更新id = 1的user
-
/user POST:新增user
上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作。
http://localhost:8888/hero/param6/zhangsan
@RequestMapping("/param6/{hname}") @ResponseBody public void param6(@PathVariable("hname") String heroName){ System.out.println(heroName); }
-
-
自定义类型转换器
SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。
但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。
自定义类型转换器的开发步骤:
(1)定义转换器类实现Converter接口
(2)在配置文件中声明转换器
(3)在<annotation-driven>中引用转换器
public class DateConverter implements Converter<String, Date> { public Date convert(String dateStr) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); Date date = null; try { date = format.parse(dateStr); } catch (ParseException e) { e.printStackTrace(); } return date; } }
<!--声明转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.cqgcxy.converter.DateConverter"></bean> </list> </property> </bean> <!--开启mvc注解驱动,并引用转换器--> <mvc:annotation-driven conversion-service="conversionService"/>
http://localhost:8888/hero/param7/2022-10-10 2008:08:08
@RequestMapping("/param7/{date}") @ResponseBody public void param7(@PathVariable("date") Date date){ System.out.println(date); }
-
Spring MVC 接收 LocalDate、LocalTime 和 LocalDateTime Java 8 时间类型参数
org.springframework.web.bind.annotation.RequestParam org.springframework.format.annotation.DateTimeFormat
-
RequestParam 比较常见,用于标注 Controller 中方法的参数;
-
DateTimeFormat 用于声明一个对象属性或者方法参数会被格式化为日期或时间。两个注解结合使用时,Spring 会调用 FormattingConversionService.convert(Object, TypeDescriptor, TypeDescriptor) 将日期时间字符串转换成日期时间类型。
示例如下:
- 将 2020-01-29 转换成 LocalDate
//http://localhost:8888/hero/param9/2022-02-02 @RequestMapping("/param8/{date}") @ResponseBody public void param8(@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @PathVariable("date") LocalDate date){ System.out.println(date); } //http://localhost:8888/hero/param9?date=2022-02-02 @RequestMapping("/param9") @ResponseBody public void param9(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date){ System.out.println(date); }
- 将 12:25:30 转换成 LocalTime
//http://localhost:8888/hero/param11?time=08:08:08 @RequestMapping("/param10") @ResponseBody public void param10(@RequestParam @DateTimeFormat(pattern = "HH:mm:ss") LocalTime time){ System.out.println(time); }
- 将 2020-01-29 12:25:30 转换成 LocalDateTime
//http://localhost:8888/hero/param11?dateTime=2022-08-08 08:08:08 @RequestMapping("/param11") @ResponseBody public void param11(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime dateTime){ System.out.println(dateTime); }
-
-
获取请求头
(1)@RequestHeader
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestHeader注解的属性如下:
- value:请求头的名称
- required:是否必须携带此请求头
@RequestMapping("/param12") @ResponseBody public void param12(@RequestHeader(value = "user-Agent",required = false) String userAgent){ System.out.println(userAgent); }
(2)@CookieValue
使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:
- value:指定cookie的名称
- required:是否必须携带此cookie
@RequestMapping("/param13") @ResponseBody public void param13(@CookieValue("JSESSIONID") String jSessionId){ System.out.println(jSessionId); }
(五)请求文件上传
-
文件上传客户端三要素
- 表单项type=“file”
- 表单的提交方式是post
- 表单的enctype属性是多部分表单形式,及enctype= “multipart/form-data"
<form action="${pageContext.request.contextPath}/hero/upload" method="post" enctype="multipart/form-data"> <p>英雄名称:<input type="text" name="heroName"></p> <p>英雄图片:<input type="file" name="heroImg"></p> <p><input type="submit" value="提交"></p> </form>
-
文件上传原理
- 当form表单修改为多部分表单时,request.getParameter()将失效。
- enctype= “application/x-www-form-urlencoded”时,form表单的正文内容格式是:key=value&key=value&key=value
- 当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成多部分形式(火狐浏览器可查看):
-----------------------------28609777630614176314163519108 Content-Disposition: form-data; name="heroName" 11 -----------------------------28609777630614176314163519108 Content-Disposition: form-data; name="heroImg"; filename="待办事项.txt" Content-Type: text/plain 1、8月4日周五授课 2、项目实训 -----------------------------28609777630614176314163519108--
-
单文件上传步骤
(1)导入fileupload和io坐标
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
(2)配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="5242880"></property> <property name="maxUploadSizePerFile" value="5242880"></property> <property name="defaultEncoding" value="UTF-8"></property> </bean>
(3)编写文件上传代码
@RequestMapping("/upload") @ResponseBody public void upload(String heroName, MultipartFile heroImg) throws IOException { String originalFilename = heroImg.getOriginalFilename(); System.out.println("文件名称:"+originalFilename); heroImg.transferTo(new File("D:\\DevelopWork\\javaweb\\springmvc-demo\\web\\upload\\"+originalFilename)); }
-
多文件上传实现
<form action="${pageContext.request.contextPath}/hero/uploads" method="post" enctype="multipart/form-data"> <p>英雄名称:<input type="text" name="heroName"></p> <p>英雄图片1:<input type="file" name="heroImgs"></p> <p>英雄图片2:<input type="file" name="heroImgs"></p> <p>英雄图片3:<input type="file" name="heroImgs"></p> <p>英雄图片4:<input type="file" name="heroImgs"></p> <p><input type="submit" value="提交"></p> </form>
或者:
<form action="${pageContext.request.contextPath}/hero/uploads" method="post" enctype="multipart/form-data"> <p>英雄名称:<input type="text" name="heroName"></p> <p>英雄图片:<input type="file" name="heroImgs" multiple></p> <p><input type="submit" value="提交"></p> </form>
@RequestMapping("/uploads") @ResponseBody public void uploads(String heroName, MultipartFile[] heroImgs) throws IOException { for (MultipartFile heroImg : heroImgs) { String originalFilename = heroImg.getOriginalFilename(); System.out.println("文件名称:"+originalFilename); heroImg.transferTo(new File("D:\\DevelopWork\\javaweb\\springmvc-demo\\web\\upload\\"+originalFilename)); } }
(六)拦截器
-
拦截器的作用
Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
-
拦截器和过滤器的区别
区别 过滤器 拦截器 使用范围 是servlet规范中的一部分,任何Java Web工程都可以使用 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用 拦截范围 在url-pattern中配置了/*之后,可以对所有要访问的资源拦截 只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js 是不会进行拦截的 -
拦截器的快速入门
(1)创建拦截器类实现Handlerlnterceptor接口
public class MyIntercepter implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("目标方法执行之前执行···"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("目标方法执行之后,视图对象返回之前执行···"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("流程都执行完毕之后执行···"); } }
(2)配置拦截器
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/hero/*"/> <bean class="com.cqgcxy.interceptor.MyIntercepter"></bean> </mvc:interceptor> </mvc:interceptors>
(3)测试拦截器的拦截效果
-
拦截器方法说明
方法名 说明 preHandle 方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法 postHandle 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作 afterCompletion 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用 -
练习:
案例需求:使用拦截器,验证用户是否登录。
- 当访问/hero/路径下的子路径时会被自定义的拦截器所拦截。该拦截器从session中判断是否有已经登录的信息,如果有则放行,如果没有,则跳转到登录页面。
public class LoginIntercepter implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); String username = (String)session.getAttribute("username"); if(StringUtils.hasText(username)){ return true; } response.sendRedirect(request.getContextPath()+"/hero/login"); return false; } }
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/hero/*"/> <mvc:exclude-mapping path="/hero/login"/> <bean class="com.cqgcxy.interceptor.LoginIntercepter"></bean> </mvc:interceptor> </mvc:interceptors>
@RequestMapping(value = "/login",method = RequestMethod.GET) public String login(){ return "forward:/login.jsp"; } @RequestMapping(value = "/login",method = RequestMethod.POST) public String login(String username, String password, HttpSession session){ //模拟登录验证 if("pep".equals(username)&&"123".equals(password)){ session.setAttribute("username",username); } return "redirect:/hero/index"; }
<form action="${pageContext.request.contextPath}/hero/login" method="post"> <p> 账号:<input type="text" name="username"> </p> <p> 密码:<input type="text" name="password"> </p> <p> <input type="submit" value="登录"> </p> </form>
(七)异常处理
-
异常处理思路
-
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。
-
系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理.
-
-
异常处理两种方式
(1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver
(2)实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器
-
简单异常处理器SimpleMappingExceptionResolver
SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="defaultErrorView" value="error"></property> <property name="exceptionMappings"> <map> <entry key="java.lang.ArithmeticException" value="error1"></entry> <entry key="java.lang.ClassCastException" value="error2"> </entry> </map> </property> </bean>
-
自定义异常处理步骤
(1)创建异常处理器类实现HandlerExceptionResolver
public class MyResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { //创建要跳转的错误视图页面 ModelAndView modelAndView = new ModelAndView(); if(e instanceof ArithmeticException){ modelAndView.addObject("info","算数异常"); }else if(e instanceof ClassCastException){ modelAndView.addObject("info","类转换异常"); } modelAndView.setViewName("error"); return modelAndView; } }
(2)配置异常处理器
<bean class="com.cqgcxy.resolver.MyResolver"></bean>
(3)编写异常页面
(4)测试异常跳转
(八)注解模式(*)
-
web.xml替换
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } @Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; } @Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); return new Filter[]{characterEncodingFilter}; } }
-
springmvc.xml替换
@Configuration //@ComponentScan({"com.cqgcxy.controller","com.cqgcxy.service"}) @ComponentScan("com.cqgcxy.controller") public class SpringMvcConfig { }
-
applicationContext.xml替换
@Configuration //@ComponentScan({"com.cqgcxy.service","com.cqgcxy.dao"}) @ComponentScan(value = "com.cqgcxy", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { }
-
拦截器配置
-
添加扫描配置类所在的配置包和拦截器包
@ComponentScan({"com.cqgcxy.controller","com.cqgcxy.config","com.cqgcxy.interceptor"})
-
把创建的拦截器通过注解@Component放入spring容器中
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Autowired private LoginIntercepter loginIntercepter; @Autowired private MyIntercepter myIntercepter; @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myIntercepter).addPathPatterns("/hero","/hero/**"); registry.addInterceptor(loginIntercepter).addPathPatterns("/hero","/hero/**").excludePathPatterns("/hero/login"); } }
或者:
@Configuration public class SpringMvcSupport implements WebMvcConfigurer { @Autowired private LoginIntercepter loginIntercepter; @Autowired private MyIntercepter myIntercepter; //使用默认的servlet处理静态资源 default-servlet-handler public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myIntercepter).addPathPatterns("/hero","/hero/**"); registry.addInterceptor(loginIntercepter).addPathPatterns("/hero","/hero/**").excludePathPatterns("/hero/login"); } }
-
-
使用默认的servlet处理静态资源 default-servlet-handler
@Configuration public class SpringMvcSupport implements WebMvcConfigurer { //使用默认的servlet处理静态资源 default-servlet-handler public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
-
配置视图解析器
@Bean public ViewResolver getViewResolver(){ InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }
-
配置文件上传解析器
@Bean public MultipartResolver multipartResolver(){ CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(); commonsMultipartResolver.setMaxUploadSize(5242880); commonsMultipartResolver.setMaxUploadSizePerFile(5242880); commonsMultipartResolver.setDefaultEncoding("UTF-8"); return commonsMultipartResolver; }
-
配置异常处理器
- 简单异常处理器
@Bean public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){ SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); exceptionResolver.setDefaultErrorView("error"); Properties properties = new Properties(); properties.setProperty("java.lang.ArithmeticException","error1"); properties.setProperty("java.lang.ClassCastException","error2"); exceptionResolver.setExceptionMappings(properties); return exceptionResolver; }
- 自定义异常处理器
@Bean public HandlerExceptionResolver getMyResolver(){ return new MyResolver(); }
(九)课后练习
- 点击主页查询链接发送请求到服务器端
- 创建BookController和showList()方法
- 创建BookService和showList()方法
- 创建BookDao和findAll()方法
- 使用JdbcTemplate完成查询操作
- 将查询数据存储到Model中
- 转发到book-list.jsp页面进行展示
三、Mybatis
(一)入门
-
概念
-
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
-
mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
-
最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
-
-
开发步骤
(1)添加相关坐标
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
(2)创建数据库表格和对应实体类XXX
(3)编写映射文件XXXMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace=""> </mapper>
(4)编写核心配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- 配置文件的根元素 --> <configuration> <!-- 属性:定义配置外在化 --> <properties></properties> <!-- 设置:定义mybatis的一些全局性设置 --> <settings> <!-- 具体的参数名和参数值 --> <setting name="" value=""/> </settings> <!-- 类型名称:为一些类定义别名 --> <typeAliases></typeAliases> <!-- 类型处理器:定义Java类型与数据库中的数据类型之间的转换关系 --> <typeHandlers></typeHandlers> <!-- 对象工厂 --> <objectFactory type=""></objectFactory> <!-- 插件:mybatis的插件,插件可以修改mybatis的内部运行规则 --> <plugins> <plugin interceptor=""></plugin> </plugins> <!-- 环境:配置mybatis的环境 --> <environments default=""> <!-- 环境变量:可以配置多个环境变量,比如使用多数据源时,就需要配置多个环境变量 --> <environment id=""> <!-- 事务管理器 --> <transactionManager type="JDBC"></transactionManager> <!-- 数据源 --> <dataSource type="POOLED"></dataSource> </environment> </environments> <!-- 数据库厂商标识 --> <databaseIdProvider type=""></databaseIdProvider> <!-- 映射器:指定映射文件或者映射类 --> <mappers></mappers> </configuration>
(5)测试
//加裁核心义件 InputStream inputStream = Resources.getResounceAsStream( "sqlMapConfig.xml"); //获取SqlSession工厂对象 SqlSessionFactory sqLSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获得SqlSession对象 sqlsession sqlsession = sqlSessionFactory.opensession(); //执行sql语句 List<User> userList = sqlSession.selectList( "userMapper.findAll"); //打印结果 system.out.println(userList); //释放资源 sqlSession.close();
(二)增删改查
注意事项:
- 增删改查语句使用对应标签
- 有参数的指定参数类型、有返回值的指定返回值类型
- Sql语句中使用#{实体属性名}方式引用实体中的属性值
- 操作使用的API是sqlSession.对应方法(“命名空间.id”[,实体对象]);
- 涉及数据库数据变化的要使用sqlSession对象显示的提交事务,即sqISession.commit()
(三)核心配置文件常用配置
-
properties标签:该标签可以加载外部的properties文件
<properties resource="jdbc.properties"></properties>
-
typeAliases标签:设置类型别名
<typeAlias type="com.cqgcxy.entity.User" alias="user"></typeAlias>
-
settings:功能设置
- 开启驼峰命名转换
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
-
mappers标签:加载映射配置
<!--使用相对于类路径的资源引用,例如: --> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <!--使用完全限定资源定位符(URL),例如: --> <mapper url="file:///var/mappers/AuthorMapper.xml" /> <!--使用映射器接口实现类的完全限定类名,例如: --> <mapper class="org.mybatis.builder.AuthorMapper"/> <!--将包内的映射器接口实现全部主册为映射器,例如: --> <package name="org.mybatis.builder"/>
-
environments标签:环境配置
- 其中,事务管理器(transactionManager)类型有两种:
- JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
- MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。
- 其中,数据源(dataSource)类型有三种:
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来。
- JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
- 其中,事务管理器(transactionManager)类型有两种:
(四)响应API
-
Resources 工具类,这个类在org.apache.ibatis.io包中。Resources从类路径下、文件系统或一个web URL中加载资源文件。
-
SqlSessionFactory build(InputStream inputStream)加载mybatis的核心文件输入流构建一个SqlSessionFactory对象。
-
SqlSessionFactory有多个个方法创建sqlSession实例。常用的有如下两个:
-
openSession()默认开启一个事务,不会自动提交,如更新数据需要手动提交事务。
-
openSession(boolean autoCommit)参数如果设置为true则自动提交事务。
-
-
SqlSession会话对象
SqlSession实例在MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例
的方法。
执行语句的方法主要有:<T> T selectone(string statement,object parameter) <E> List<E> selectList (string statement,object parameter) int insert (string statement,object parameter) int update (string statement,object parameter) int delete (string statement,object parameter)
操作事务的方法主要有:
void commit() void rollback()
(五)dao层实现
-
传统开发方式
public interface UserMapper { List<User> findAll() throws IOException; }
public class UserMapperImpl implements UserMapper { public List<User> findAll() throws IOException{ InputStream inputStream = Resources.getResounceAsStream( "sqlMapConfig.xml"); SqlSessionFactory sqLSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); sqlsession sqlsession = sqlSessionFactory.opensession(); List<User> userList = sqlSession.selectList( "userMapper.findAll"); sqlSession.close(); return userList; } }
-
代理开发方式
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
(1)Mapper.xml文件中的namespace与mapper接口的全限定名相同
(2)Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
(3)Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
(4)Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
-
测试代理方式
@Test public void findAllTest() throws IOException{ InputStream inputStream = Resources.getResounceAsStream( "sqlMapConfig.xml"); SqlSessionFactory sqLSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); sqlsession sqlsession = sqlSessionFactory.opensession(); //获得MyBatis框架生成的UserMapper接口的实现类 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.findAll(); sqlSession.close(); return userList; }
(六)动态sql
-
动态SQL之<if>
我们根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
List<User> findByCondition(User user);
<select id="findByCondition" parameterType="user" resultType="user"> select * from User <where> <if test="id!=0">and id=#{id}</if> <if test="username!=null">and username=#{username}</if> </where> </select>
-
动态SQL之<foreach>
循环执行sql的拼接操作,例: SELECT*FROM USER WHERE id IN (1,2,5)。
List<User> findByIds(int[] ids);
<select id="findByIds" parameterType="list" resultType="user"> select * from User <where> <foreach collection="array" open="id in(" close=")" item="id" separator=", "> #{id} </foreach> </where> </select>
List<User> findByIds(List<In> ids);
<select id="findByIds" parameterType="list" resultType="user"> select * from User <where> <foreach collection="list" open="id in(" close=")" item="id" separator=", "> #{id} </foreach> </where> </select>
- <where> : where条件
- <if>: if判断
- <foreach>:循环
- <sql>:sql片段抽取
(七)typeHandler
无论是MyBatis在预处理语句(PreparedStatement)中设置参数时,还是从结果集中取出值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。
需求:Java中的Date数据类型,存到数据库时存成毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
步骤:
-
定义转换类继承类BaseTypeHandler
-
覆盖4个未实现的方法,其中setNonNullarameter为java程序设置数据到数据库的回调方法,getNullableResult
为查询时mysql的字符串类型转换成java的Type类型的方法public class DateTypeHandler extends BaseTypeHandler<Date> { //将java转成数据库需要类型 @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException { long time=date.getTime(); //把第i个参数设置成转后的值 preparedStatement.setLong(i,time); } //将数据库类型转成java类型 //String s:要转换的字段名称 //ResultSet resultSet:查询出的结果集 @Override public Date getNullableResult(ResultSet resultSet, String s) throws SQLException { long time=resultSet.getLong(s); return new Date(time); } //将数据库类型转成java类型 @Override public Date getNullableResult(ResultSet resultSet, int i) throws SQLException { long time = resultSet.getLong(i); return new Date(time); } //将数据库类型转成java类型 @Override public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException { long time = callableStatement.getLong(i); return new Date(time); } }
-
在MyBatis核心配置文件中进行注册
<!-- 类型处理器:定义Java类型与数据库中的数据类型之间的转换关系 --> <typeHandlers> <typeHandler handler="com.cqgcxy.handler.DateTypeHandler"></typeHandler> </typeHandlers>
-
测试转换是否正确
(八)plugins
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。
步骤:
-
导入通用PageHelper的坐标
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version> </dependency> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.5</version> </dependency>
-
在mybatis核心配置文件中配置PageHelper插件
<plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"/> </plugin> </plugins>
-
测试分页数据获取
public class HeroMapperTest { @Test public void findAll() throws IOException { InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession sqlSession = sqlSessionFactory.openSession(); HeroMapper heroMapper = sqlSession.getMapper(HeroMapper.class); //启动分页并设置参数 PageHelper.startPage(1, 2); List<Hero> heroList = heroMapper.findAll(); heroList.forEach(s-> System.out.println(s)); //获得分页相关数据 PageInfo<Hero> heroPageInfo = new PageInfo<>(heroList); System.out.println("当前页:"+heroPageInfo.getPageNum()); System. out.println("每页显示条数: "+heroPageInfo.getPageSize()); System. out. println("总条数: "+heroPageInfo.getTotal()); System. out. println("总页数: "+heroPageInfo. getPages()); System. out.println("上一页: "+heroPageInfo.getPrePage()); System. out.println("下一页: "+heroPageInfo. getNextPage()) ; System. out.println("是否是第一个: "+heroPageInfo.isIsFirstPage()); System. out. println("是否是最后一个: "+heroPageInfo. isIsLastPage()); sqlSession.close(); } }
(九)多表操作
- 一对一
public class Order {
private Long id;
private BigDecimal totalPrice;
private Date orderDate;
//一对一
private Hero hero;
//···
}
//方式一:
<resultMap id="orderMap" type="com.cqgcxy.entity.Order">
<id column="id" property="id"></id>
<id column="total_price" property="totalPrice"></id>
<id column="order_date" property="orderDate"></id>
<id column="hid" property="hero.id"></id>
<id column="hero_name" property="hero.heroName"></id>
<id column="create_time" property="hero.createTime"></id>
</resultMap>
<select id="findAll" resultMap="orderMap">
select o.*,h.id hid,h.hero_name,h.create_time from t_order o,t_hero h where o.hid=h.id
</select>
//方式二:
<resultMap id="orderMap" type="com.cqgcxy.entity.Order">
<id column="id" property="id"></id>
<id column="total_price" property="totalPrice"></id>
<id column="order_date" property="orderDate"></id>
<!-- property:当前实体(Hero)中的属性名称(private Hero hero)-->
<!-- javaType:当前实体(Hero)中的属性类型(com.cqgcxy.entity.Hero)-->
<association property="hero" javaType="com.cqgcxy.entity.Hero">
<id column="hid" property="id"></id>
<id column="hero_name" property="heroName"></id>
<id column="create_time" property="createTime"></id>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
select o.*,h.id hid,h.hero_name,h.create_time from t_order o,t_hero h where o.hid=h.id
</select>
-
一对多
public class Hero { private Long id; private String heroName; private Date createTime; private List<Order> orderList; //··· }
<resultMap id="heroMap" type="com.cqgcxy.entity.Hero"> <id column="id" property="id"></id> <result column="hero_name" property="heroName"></result> <result column="create_time" property="createTime"></result> <collection property="orderList" ofType="com.cqgcxy.entity.Order"> <id column="id" property="id"></id> <id column="total_price" property="totalPrice"></id> <id column="order_date" property="orderDate"></id> </collection> </resultMap> <select id="findAll" resultMap="heroMap"> select h.*,o.id oid,o.total_price,o.order_date from t_order o,t_hero h where o.hid=h.id </select>
-
多对多
**注意:**多对多实现同一对多。不同点在于,多对多至少是三张表之前的关系(包含关联表)。
(十)注解开发(*)
@lnsert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
注意:mapper配置不用在配置mapper文件地址,而是配置包的路径。
<mappers>
<package name="com.cqgcxy.dao"/>
</mappers>
-
一对一
//方式一: @Select("select o.*,h.id hid,h.hero_name,h.create_time from t_order o,t_hero h where o.hid=h.id") @Results({ @Result(column = "id",property = "id"), @Result(column = "total_price",property = "totalPrice"), @Result(column = "order_date",property = "orderDate"), @Result(column = "hid",property = "hero.id"), @Result(column = "hero_name",property = "hero.heroName"), @Result(column = "create_time",property = "hero.createTime") }) List<Order> findAll();
//方式二: //HeroMapper.java中 @Select("select * from t_hero where id = #{id}") Hero findById(Integer id); //OrderMapper.java中 @Select("select o.*,h.id hid,h.hero_name,h.create_time from t_order o,t_hero h where o.hid=h.id") @Results({ @Result(column = "id",property = "id"), @Result(column = "total_price",property = "totalPrice"), @Result(column = "order_date",property = "orderDate"), @Result(column = "hid", property = "hero", javaType = Hero.class, one = @One(select="com.cqgcxy.dao.HeroMapper.findById") ) }) List<Order> findAll();
-
一对多
//OrderMapper.java中 @Select("select * from t_order where id =#{id}") Order findById(Integer id); //HeroMapper.java中 @Select("select h.*,o.id oid,o.total_price,o.order_date from t_order o,t_hero h where o.hid=h.id") @Results({ @Result(column = "id",property = "id"), @Result(column = "hero_name",property = "heroName"), @Result(column = "create_time",property = "createTime"), @Result(column = "oid", property = "orderList", javaType = List.class, many = @Many(select = "com.cqgcxy.dao.OrderMapper.findById") ) }) List<Hero> findAll();
-
多对多
**注意:**多对多实现同一对多。不同点在于,多对多@Many中的查询语句要根据条件id查询包括关联表在内的两张表。
-
动态sql
@Select({"<script>", "select * from t_hero", "<where>", "<if test='id != null'>", " and id = #{id}", "</if>", "<if test='heroName != null'>", " and hero_name = #{heroName}", "</if>", "</where>", "</script>" }) List<Hero> findByCondition(Hero hero);
四、SSM整合
(一)xml方式整合
-
导入坐标依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.4</version> </dependency>
-
编写实体类
-
编写mapper接口
-
编写service接口和实现类
-
编写控制器
-
整合配置文件
- web.xml
<!--向核心控制器告知spring的配置文件在哪--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <!--配置spring的监听器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <servlet-name>dispatcherServlet</servlet-name> </filter-mapping>
-
springmvc.xml
<context:component-scan base-package="com.cqgcxy.controller"></context:component-scan> <mvc:annotation-driven />
-
applicationContext.xml
<context:component-scan base-package="com.cqgcxy.service"></context:component-scan> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="ds" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value= "${jdbc.driver}" /> <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="dataSource" ref="ds" /> <property name="typeAliasesPackage" value="com.cqgcxy.entity"/> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true"/> </bean> </property> </bean> <bean class=" org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.cqgcxy.mapper"/> </bean>
(二)注解方式整合
-
创建Web项目入口配置类替换web.xml
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer { //加载Spring配置类 protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } //加载SpringMVC配置类 protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } //设置SpringMVC请求地址拦截规则 protected String[] getServletMappings() { return new String[]{"/"}; } //设置post请求中文乱码过滤器 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("utf-8"); return new Filter[]{filter}; } }
-
创建SpringMVC配置类替换springmvc.xml
@Configuration @ComponentScan("com.cqgcxy.controller") @EnableWebMvc public class SpringMvcConfig { }
-
创建SpringConfig配置类替换applicationContext.xml
@Configuration @ComponentScan({"com.cqgcxy.service"}) @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MyBatisConfig.class}) @EnableTransactionManagement public class SpringConfig { }
-
创建JdbcConfig配置类
public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } @Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager ds = new DataSourceTransactionManager(); ds.setDataSource(dataSource); return ds; } }
-
创建MybatisConfig配置类
public class MyBatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("com.cqgcxy.entity"); //用于处理数据库表下划线跟实体类的字段名称不一致问题 Configuration configuration = new Configuration(); configuration.setMapUnderscoreToCamelCase(true); factoryBean.setConfiguration(configuration); return factoryBean; } @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.cqgcxy.mapper"); return msc; } }
.cqgcxy.service"})
@PropertySource(“classpath:jdbc.properties”)
@Import({JdbcConfig.class,MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
4. 创建JdbcConfig配置类
```java
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
-
创建MybatisConfig配置类
public class MyBatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("com.cqgcxy.entity"); //用于处理数据库表下划线跟实体类的字段名称不一致问题 Configuration configuration = new Configuration(); configuration.setMapUnderscoreToCamelCase(true); factoryBean.setConfiguration(configuration); return factoryBean; } @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.cqgcxy.mapper"); return msc; } }