springmvc详解(三):注解开发基础和参数绑定
目录
2.2、持久层开发(开发mapper.java和mapper.xml)
在springmvc详解(二):springmvc和mybatis整合文章中我们已经初步完成了完成的配置和代码的编写,实现了请求查询商品的功能。接下来,我们将以在客户端修改商品参数并提交到后台的案例为媒介,讲解参数绑定的内容。
一、什么是参数绑定
客户在浏览器端会提交一些参数到服务器端,比如用户的登录等,就会传username 和 password过来,springmvc则通过参数绑定组件将请求参数的内容进行数据转换,然后将转换后的值赋给controller方法的形参,这就是参数绑定的过程,其实,springmvc就是使用controller方法形参来接收请求的参数。
二、修改商品参数案例
2.1、功能需求
在springmvc详解(二):springmvc和mybatis整合文章中我们已经完成了商品的查询功能,现在我们在查询得到的商品页面上实现修改商品品参数的功能,即点击相应的修改按钮进入修改页面,修改结束后将所修改的结果提交到数据库并进行更新。
操作流程如下:
1、进入商品查询列表页面
2、点击修改,进入商品修改页面,页面中显示了要修改的商品(从数据库查询)
要修改的商品从数据库查询,根据商品id(主键)查询商品信息
3、在商品修改页面,修改商品信息,修改后,点击提交
2.2、持久层开发(开发mapper.java和mapper.xml)
我们可以利用逆向工程所生成的单表的mapper.java和mapper.xml中的方法:(关于逆向工程的知识,详见MyBatis详解:逆向工程自动生成代码)
1、根据id查询商品信息:Items.java中的Items selectByPrimaryKey(Integer id)方法
2、根据id更新Items表的数据:Items.java中的int updateByPrimaryKeyWithBLOBs(Items record)方法
2.3、业务层开发(开发service接口和对应的实现类)
2.3.1、service接口
package cn.itcast.ssm.service;
import java.util.List;
import cn.itcast.ssm.po.ItemsCustom;
import cn.itcast.ssm.po.ItemsQueryVo;
public interface ItemsService {
//商品查询列表
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;
//根据id查询商品信息(使用扩展类)
public ItemsCustom findItemsById(Integer id) throws Exception;
//修改商品信息
public void updateItems(Integer id,ItemsCustom itemsCustom) throws Exception;
}
2.3.2、service接口的实现类
package cn.itcast.ssm.service.impl;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.itcast.ssm.mapper.ItemsMapper;
import cn.itcast.ssm.mapper.ItemsMapperCustom;
import cn.itcast.ssm.po.Items;
import cn.itcast.ssm.po.ItemsCustom;
import cn.itcast.ssm.po.ItemsQueryVo;
import cn.itcast.ssm.service.ItemsService;
@Service("itemsService")
public class ItemsServiceImpl implements ItemsService{
@Autowired
private ItemsMapperCustom itemsMapperCustom;
@Autowired
private ItemsMapper itemsMapper;
//商品查询列表
@Override
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception {
return itemsMapperCustom.findItemsList(itemsQueryVo);
}
//根据id查询商品信息(使用扩展类)
@Override
public ItemsCustom findItemsById(Integer id) throws Exception {
Items items = itemsMapper.selectByPrimaryKey(id);
//中间对商品信息进行业务处理
//....
//返回ItemsCustom
ItemsCustom itemsCustom = new ItemsCustom();
//将items的属性值拷贝到itemsCustom
BeanUtils.copyProperties(items, itemsCustom);
return itemsCustom;
}
//修改商品信息
@Override
public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception {
//添加业务校验,通常在service接口对关键参数进行校验
//校验 id是否为空,如果为空抛出异常
//更新商品信息使用updateByPrimaryKeyWithBLOBs根据id更新items表中所有字段,包括 大文本类型字段
//updateByPrimaryKeyWithBLOBs要求必须转入id
itemsCustom.setId(id);
itemsMapper.updateByPrimaryKeyWithBLOBs(itemsCustom);
}
}
2.4、表现层(开发controller和jsp)
2.4.1、开发controller
package cn.itcast.ssm.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import cn.itcast.ssm.po.Items;
import cn.itcast.ssm.po.ItemsCustom;
import cn.itcast.ssm.po.ItemsQueryVo;
import cn.itcast.ssm.service.ItemsService;
//商品的controller
@Controller
//为了对url进行分类管理 ,可以在这里定义根路径,最终访问url是根路径+子路径
//比如:商品列表:/items/queryItems.action
@RequestMapping("/items")
public class ItemsController {
@Autowired
private ItemsService itemsService;
@RequestMapping(value="/editItems",method={RequestMethod.POST,RequestMethod.GET})
//@RequestParam里边指定request传入参数名称和形参进行绑定。
//通过required属性指定参数是否必须要传入
//通过defaultValue可以设置默认值,如果id参数没有传入,将默认值和形参绑定。
//1、默认支持的参数类型绑定2、简单类型的参数绑定
public String editItems(Model model,@RequestParam(value="id",required=true) Integer items_id)throws Exception {
//调用service根据商品id查询商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(items_id);
//通过形参中的model将model数据传到页面
//相当于modelAndView.addObject方法
model.addAttribute("itemsCustom", itemsCustom);
return "items/editItems";
}
//商品信息修改提交
@RequestMapping("/editItemsSubmit")
//3、pojo绑定
public String editItemsSubmit(Model model,Integer id,
ItemsCustom itemsCustom)throws Exception {
//方法形参传入的是ItemsCustom,如果再传入一个User,他们有相同的属性name,此时页面传过来的input里面的name该和谁的水性绑定。
//所以用包装类型作为形参更好
//调用service更新商品信息,页面需要将商品信息传到此方法
itemsService.updateItems(id, itemsCustom);
return "success";
}
}
在controller的商品信息修改页面显示和商品信息修改提交两个方法如下:
这两个方法的形参其实就是通过参数绑定所实现的,修改某个商品的参数需要先跳到修改页面显示出该商品的当前参数,其中调用的就是业务层的根据id查询商品信息方法;修改完商品参数以后需要将新的商品信息提交到数据库进行更新,商品信息修改提交方法调用的就是业务层的根据id更新Items表的数据。
我们需要讲解得就是如何实现参数绑定,editItems方法中涉及到默认支持的参数绑定类型(Model model)和简单类型的参数绑定(@RequestParam(value="id",required=true) Integer items_id),editItemsSubmit方法涉及到默认支持的参数绑定类型(Model model)和简单类型(Integer id)和pojo类型(ItemsCustom itemsCustom)的参数绑定。详见下面三、参数绑定的类型。
2.4.2、开发jsp
跳转到单个商品信息修改页面的jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>修改商品信息</title>
</head>
<body>
<!-- 显示错误信息 -->
<c:if test="${allErrors!=null }">
<c:forEach items="${allErrors }" var="error">
${ error.defaultMessage}<br/>
</c:forEach>
</c:if>
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemsSubmit.action" method="post" >
<input type="hidden" name="id" value="${itemsCustom.id }"/>
修改商品信息:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td><input type="text" name="name" value="${itemsCustom.name }"/></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="price" value="${itemsCustom.price }"/></td>
</tr>
<tr>
<td>商品生产日期</td>
<td><input type="text" name="createtime" value="<fmt:formatDate value="${itemsCustom.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
</tr>
<%-- <tr>
<td>商品图片</td>
<td>
<c:if test="${item.pic !=null}">
<img src="/pic/${item.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="pictureFile"/>
</td>
</tr> --%>
<tr>
<td>商品简介</td>
<td>
<textarea rows="3" cols="30" name="detail">${itemsCustom.detail }</textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交"/>
</td>
</tr>
</table>
</form>
</body>
</html>
修改商品信息成功提交后的页面jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>成功提示</title>
</head>
<body>
操作成功!
</body>
</html>
三、参数绑定的类型
3.1、spring参数绑定过程
从客户端请求key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上。springmvc中,接收页面提交的数据是通过方法形参来接收。而不是在controller类定义成员变量接收!!!!(servlet是这种方式)
其原理如下:
需要重点说明的是:spring参数绑定过程是处理器适配器调用springmvc提供参数绑定组件将key/value数据转成controller方法的形参(先转成形参,再赋给形参),这一过程都是spring帮我们自动完成的,我们只需要遵守规则(即下面讲解的不同类型的参数在controller方法的形参处该如何绑定)
3.2、默认支持的参数绑定类型
直接在controller方法形参上定义下边类型的对象,就可以使用这些对象,适配器调用参数绑定组件直接进行绑定。在参数绑定过程中,如果遇到下边类型直接进行绑定。可以看到2.4.1中都用到了默认支持的Model。
1、HttpServletRequest
通过request对象获取请求信息
2、HttpServletResponse
通过response处理响应信息
3、HttpSession
通过session对象得到session中存放的对象
4、Model/ModelMap
model是一个接口,modelMap是一个接口实现 。
作用:将model数据填充到request域。
2.4.1中的editItems方法和editItemsSubmit方法都用到了Model。
3.3、简单类型
简单类型包括:Integer、String、Float、Long、Double等类型,但不包括日期类型(日期类型会单独讲解)
绑定规则:
通过@RequestParam对简单类型的参数进行绑定。
如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。
如果使用@RequestParam,不用限制request传入参数名称和controller方法的形参名称一致。
通过required属性指定参数是否必须要传入,如果设置为true,没有传入参数,报下边错误:
2.4.1中的editItems方法的形参:
对应于itemsList.jsp中的提交按钮:
若不使用@RequestParam,则editItems方法的形参需要和request传入参数名称为id保持一致;而使用了@RequestParam就可以将request传入参数名称为id与editItems方法的形参Integer items_id进行对应,达到修改参数名称的效果。
3.4、pojo类型
3.4.1、绑定规则:
pojo作为controller的形参时,jsp页面中input的name和controller的pojo形参中的属性名称一致,即可将页面中数据绑定到pojo。
2.4.1中editItemsSubmit方法的形参ItemsCustom itemsCustom即为pojo类型(继承自逆向工程生成的items),对应关系如下
jsp页面定义:商品信息修改页面editItems.jsp
controller的pojo形参ItemsCustom的定义:(由于ItemsCustom继承自逆向工程生成的Items,故ItemsCustom的属性和Items中的属性相同)
3.4.2、自定义参数绑定实现日期类型绑定
对于controller形参中pojo对象,如果属性中有日期类型,需要自定义参数绑定。
将请求日期数据串传成日期类型,要转换的日期类型和pojo中日期属性的类型保持一致。
所以自定义参数绑定将日期串转成java.util.Date类型。需要向处理器适配器中注入自定义的参数绑定组件。
自定义的日期类型的转换器:
package cn.itcast.ssm.controller.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
public class CustomDateConverter implements Converter<String,Date>{
@Override
public Date convert(String source) {
//实现 将日期串转成日期类型(格式是yyyy-MM-dd HH:mm:ss)
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
//转成直接返回
return simpleDateFormat.parse(source);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//如果参数绑定失败返回null
return null;
}
}
在springmvc的配置文件spring.xml中配置自定义的日期类型的转换器:
3.5、包装pojo
这里我们对2.4.1中的查询商品列表方法进行讲解。
quertItems方法的形参ItemsQueryVo即为包装pojo类型(其属性为pojo)
绑定规则:
包装pojo作为controller的形参时,jsp页面中input的name和包装pojo中的pojo.属性名称一致,即可将页面中数据绑定到pojo。
jsp页面定义:下旬商品信息页面itemsList.jsp
controller的包装pojo形参ItemsQueryVo的定义:
包装pojo形参ItemsQueryVo的pojo属性ItemsCustom的定义:
3.6、集合类型
关于集合类型的参数绑定,为了简单起见,我们主要讲解表现层的controller和jsp是如何实现参数绑定的。而业务层和持久层的具体方法就不做赘述了。
3.6.1、数组绑定
针对数组绑定我们实现商品批量删除功能,用户在页面选择多个商品,批量删除。
controller方法定义:
// 批量删除 商品信息(数组绑定)
@RequestMapping("/deleteItems")
public String deleteItems(Integer[] items_id) throws Exception {//简单类型参数绑定(数组)
// 调用service批量删除商品
// ...
return "success";
}
jsp页面定义:
3.6.2、list绑定
针对list绑定实现批量商品修改功能,在页面输入多个商品信息,将多个商品信息提交到controller方法中。
功能实现分析:
1、进入批量商品修改页面(editItemsQuery.jsp),在次页面通过查询商品列表功能,将所有商品信息显示出来
2、通过批量修改商品提交功能将修改后的商品数据提交到数据库。
通常在需要批量提交数据时,将提交的数据绑定到list<pojo>中,比如:成绩录入(录入多门课成绩,批量提交)。
故使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list<pojo>属性
controller方法定义:
List接收页面提交的批量数据,定义在在包装pojo中中的list<pojo>属性,故editItemsAllSubmit方法传入的形参为包装pojo
// 批量修改商品页面,将商品信息查询出来,在页面中可以编辑商品信息
@RequestMapping("/editItemsQuery")
public ModelAndView editItemsQuery(HttpServletRequest request,
ItemsQueryVo itemsQueryVo) throws Exception {
// 调用service查找 数据库,查询商品列表
List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo);
// 返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 相当 于request的setAttribut,在jsp页面中通过itemsList取数据
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("items/editItemsQuery");
return modelAndView;
}
// 批量修改商品提交(List绑定,将list封装在包装类型的属性中)
// 通过ItemsQueryVo接收批量提交的商品信息,将商品信息存储到itemsQueryVo中itemsList属性中。
@RequestMapping("/editItemsAllSubmit")
public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo)
throws Exception {
return "success";
}
jsp页面定义:
3.6.3、map绑定
在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。
包装类中定义Map对象如下:
Public class QueryVo {
private Map<String, Object> itemInfo = new HashMap<String, Object>();
//get/set方法..
}
controller方法定义:
public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
}
页面定义如下:
<tr>
<td>学生信息:</td>
<td>
姓名:<inputtype="text"name="itemInfo['name']"/>
年龄:<inputtype="text"name="itemInfo['price']"/>
.. .. ..
</td>
</tr>
四、参数绑定总结
既然springmvc帮我们自动将jsp页面提交的数据绑定到controller方法的形参中,免去了Servlet那样在方法内部通过局部变量从request处获取,我们就要遵循一定的映射规则,这样springmvc才能帮助我们自动绑定。从简单类型到pojo再到包装pojo再到集合,是主键复杂的过程,要遵循一层一层的嵌套,且在嵌套过程中要满足jsp页面中的input标签内的name和controller方法的形参的属性名称保持一致。