springmvc概述(4)(不同对象[pojo/数组/list/map]参数绑定,校验,数据回显)

1       包装类型pojo参数绑定

ps:即参数对象的属性也是对象,将所有参数封装到一个对象中(对象的属性由多个对象属性组成)

使用场景:
1,当映射的方法需要多个参数对象的时候,
2,参数对象具有相同的属性,需要对相同属性赋值
不如将所有对象参数组合成一个类,统一进行处理

1.1      需求

         商品查询controller方法中实现商品查询条件传入。

1.2      实现方法

第一种方法:

在形参中 添加HttpServletRequest request参数,通过request接收查询条件参数。

第二种方法:

在形参中让包装类型的pojo接收查询条件参数。

分析:

页面传参数的特点:复杂,多样性。条件包括 :用户账号、商品编号、订单信息。。。
如果将用户账号、商品编号、订单信息等放在简单pojo(属性是简单类型)中,pojo类属性比较多,比较乱。
建议使用包装类型的pojo,pojo中属性是pojo。

1.3      页面参数和controller方法形参定义

页面参数:

商品名称:<input name="itemsCustom.name" />  (ps:该名字对应是属性对象的属性
注意:itemsCustom和包装pojo中的属性一致即可。

controller方法形参:

public ModelAndView queryItems(HttpServletRequest request,ItemsQueryVo itemsQueryVo) throws Exception

2       集合类型绑定

2.1      数组绑定

2.1.1     需求

商品批量删除,用户在页面选择多个商品,批量删除。

2.1.2     表现层实现

关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。

controller方法定义:

@Controller
// 为了对url进行分类管理 ,可以在这里定义根路径,最终访问url是根路径+子路径
// 比如:商品列表:/items/queryItems.action
@RequestMapping("/items")
public class ItemsController {

    @Autowired
    private ItemsService itemsService;

    // 商品分类
    //itemtypes表示最终将方法返回值放在request中的key
    @ModelAttribute("itemtypes")
    public Map<String, String> getItemTypes() {

        Map<String, String> itemTypes = new HashMap<String, String>();
        itemTypes.put("101", "数码");
        itemTypes.put("102", "母婴");

        return itemTypes;
    }

    // 商品查询
    @RequestMapping("/queryItems")
    public ModelAndView queryItems(HttpServletRequest request,
            ItemsQueryVo itemsQueryVo) throws Exception {
        // 测试forward后request是否可以共享

        System.out.println(request.getParameter("id"));

        // 调用service查找 数据库,查询商品列表
        List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo);

        // 返回ModelAndView
        ModelAndView modelAndView = new ModelAndView();
        // 相当 于request的setAttribut,在jsp页面中通过itemsList取数据
        modelAndView.addObject("itemsList", itemsList);

        // 指定视图
        // 下边的路径,如果在视图解析器中配置jsp路径的前缀和jsp路径的后缀,修改为
        // modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
        // 上边的路径配置可以不在程序中指定jsp路径的前缀和jsp路径的后缀
        modelAndView.setViewName("items/itemsList");

        return modelAndView;

    }

    // 商品信息修改页面显示
    // @RequestMapping("/editItems")
    // 限制http请求方法,可以post和get
    // @RequestMapping(value="/editItems",method={RequestMethod.POST,RequestMethod.GET})
    // public ModelAndView editItems()throws Exception {
    //
    // //调用service根据商品id查询商品信息
    // ItemsCustom itemsCustom = itemsService.findItemsById(1);
    //
    // // 返回ModelAndView
    // ModelAndView modelAndView = new ModelAndView();
    //
    // //将商品信息放到model
    // modelAndView.addObject("itemsCustom", itemsCustom);
    //
    // //商品修改页面
    // modelAndView.setViewName("items/editItems");
    //
    // return modelAndView;
    // }

    @RequestMapping(value = "/editItems", method = { RequestMethod.POST,
            RequestMethod.GET })
    // @RequestParam里边指定request传入参数名称和形参进行绑定。
    // 通过required属性指定参数是否必须要传入
    // 通过defaultValue可以设置默认值,如果id参数没有传入,将默认值和形参绑定。
    public String editItems(Model model,
            @RequestParam(value = "id", required = true) Integer items_id)
            throws Exception {

        // 调用service根据商品id查询商品信息
        ItemsCustom itemsCustom = itemsService.findItemsById(items_id);
        //判断商品是否为空,根据id没有查询到商品,抛出异常,提示用户商品信息不存 在
//        if(itemsCustom == null){
//            throw new CustomException("修改的商品信息不存在!");
//        }

        // 通过形参中的model将model数据传到页面
        // 相当于modelAndView.addObject方法
        model.addAttribute("items", itemsCustom);

        return "items/editItems";
    }
    
    //查询商品信息,输出json
    ///itemsView/{id}里边的{id}表示占位符,通过@PathVariable获取占位符中的参数,
    //如果占位符中的名称和形参名一致,在@PathVariable可以不指定名称
    @RequestMapping("/itemsView/{id}")
    public @ResponseBody ItemsCustom itemsView(@PathVariable("id") Integer id)throws Exception{
        
        //调用service查询商品信息
        ItemsCustom itemsCustom = itemsService.findItemsById(id);
        
        return itemsCustom;
        
    }
    

    // 商品信息修改提交
    // 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
    // bindingResult接收校验出错信息
    // 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
    // value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
    // @ModelAttribute可以指定pojo回显到页面在request中的key
    @RequestMapping("/editItemsSubmit")
    public String editItemsSubmit(
            Model model,
            HttpServletRequest request,
            Integer id,
            @ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom,
            BindingResult bindingResult,
            MultipartFile items_pic//接收商品图片
            ) throws Exception {

        // 获取校验错误信息
        if (bindingResult.hasErrors()) {
            // 输出错误信息
            List<ObjectError> allErrors = bindingResult.getAllErrors();

            for (ObjectError objectError : allErrors) {
                // 输出错误信息
                System.out.println(objectError.getDefaultMessage());

            }
            // 将错误信息传到页面
            model.addAttribute("allErrors", allErrors);
            
            
            //可以直接使用model将提交pojo回显到页面
            model.addAttribute("items", itemsCustom);
            
            // 出错重新到商品修改页面
            return "items/editItems";
        }
        //原始名称
        String originalFilename = items_pic.getOriginalFilename();
        //上传图片
        if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){
            
            //存储图片的物理路径
            String pic_path = "F:\\develop\\upload\\temp\\";
            
            
            //新的图片名称
            String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
            //新图片
            File newFile = new File(pic_path+newFileName);
            
            //将内存中的数据写入磁盘
            items_pic.transferTo(newFile);
            
            //将新图片名称写到itemsCustom中
            itemsCustom.setPic(newFileName);
            
        }
        

        // 调用service更新商品信息,页面需要将商品信息传到此方法
        itemsService.updateItems(id, itemsCustom);

        // 重定向到商品查询列表
        // return "redirect:queryItems.action";
        // 页面转发
        // return "forward:queryItems.action";
        return "success";
    }

    // 批量删除 商品信息
    @RequestMapping("/deleteItems")
    public String deleteItems(Integer[] items_id) throws Exception {

        // 调用service批量删除商品
        // ...

        return "success";

    }

    // 批量修改商品页面,将商品信息查询出来,在页面中可以编辑商品信息
    @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;

    }

    // 批量修改商品提交
    // 通过ItemsQueryVo接收批量提交的商品信息,将商品信息存储到itemsQueryVo中itemsList属性中。
    @RequestMapping("/editItemsAllSubmit")
    public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo)
            throws Exception {

        return "success";
    }

}
ItemsController.java

页面定义:

ps:下面省略了表单的提交的标签内容

 

<%@ 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>
<script type="text/javascript">
function deleteItems(){
    //提交form
    document.itemsForm.action="${pageContext.request.contextPath }/items/deleteItems.action";
    document.itemsForm.submit();
}
function queryItems(){
    //提交form
    document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action";
    document.itemsForm.submit();
}
</script>
</head>
<body> 
当前用户:${username },
<c:if test="${username!=null }">
 <a href="${pageContext.request.contextPath }/logout.action">退出</a>
</c:if>
<form name="itemsForm" action="${pageContext.request.contextPath }/items/queryItems.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td>
商品名称:<input name="itemsCustom.name" />
商品类型:
<select name="itemtype">
    <c:forEach items="${itemtypes }" var="itemtype">
        <option value="${itemtype.key }">${itemtype.value }</option>        
    </c:forEach>
</select>

</td>
<td><input type="button" value="查询" onclick="queryItems()"/>
<input type="button" value="批量删除" onclick="deleteItems()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
    <td>选择</td>
    <td>商品名称</td>
    <td>商品价格</td>
    <td>生产日期</td>
    <td>商品描述</td>
    <td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item">
<tr>    
    <td><input type="checkbox" name="items_id" value="${item.id}"/></td>
    <td>${item.name }</td>
    <td>${item.price }</td>
    <td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
    <td>${item.detail }</td>
    
    <td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td>

</tr>
</c:forEach>

</table>
</form>
</body>

</html>
itemsList.jsp

2.2      list绑定

2.2.1     需求

通常在需要批量提交数据时,将提交的数据绑定到list<pojo>中,比如:成绩录入(录入多门课成绩,批量提交),

本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。

2.2.2     表现层实现

controller方法定义:

1、进入批量商品修改页面(页面样式参考商品列表实现)
2、批量修改商品提交
         使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list<pojo>属性

    

页面定义:

 

(ps: 以上作为多条文本框,表单提交)

1.1      map绑定

也通过在包装pojo中定义map类型属性。

在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。

包装类中定义Map对象如下:

Public class QueryVo {
private Map<String, Object> itemInfo = new HashMap<String, Object>();
  //get/set方法..
}

页面定义如下:

<tr>
<td>学生信息:</td>
<td>
姓名:<inputtype="text"name="itemInfo['name']"/>
年龄:<inputtype="text"name="itemInfo['price']"/>
.. .. ..
</td>
</tr>

Contrller方法定义如下:

public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
}

3       springmvc校验

3.1      校验理解

项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。

服务端校验:

控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
持久层dao:一般是不校验的。

3.2      springmvc校验需求

springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

    <!-- 可以扫描controller、service、...
    这里让扫描controller,指定controller的包
     -->
    <context:component-scan base-package="cn.itcast.ssm.controller"></context:component-scan>
    
    <!-- 静态资源解析
    包括 :js、css、img、..
     -->
     <mvc:resources location="/js/" mapping="/js/**"/>
     <mvc:resources location="/img/" mapping="/img/**"/>
        
    <!--注解映射器 -->
    <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> -->
    <!--注解适配器 -->
    <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> -->
    
    <!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置
    mvc:annotation-driven默认加载很多的参数绑定方法,
    比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上边的RequestMappingHandlerMapping和RequestMappingHandlerAdapter
    实际开发时使用mvc:annotation-driven
     -->
    <mvc:annotation-driven conversion-service="conversionService"
    validator="validator"></mvc:annotation-driven>
    
    <!-- 视图解析器
    解析jsp解析,默认使用jstl标签,classpath下的得有jstl的包
     -->
    <bean        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置jsp路径的前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 配置jsp路径的后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>
    
    <!-- 自定义参数绑定 -->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <!-- 转换器 -->
        <property name="converters">
            <list>
                <!-- 日期类型转换 -->
                <bean class="cn.itcast.ssm.controller.converter.CustomDateConverter"/>
            </list>
        </property>            
    </bean>
    
    <!-- 校验器 -->
    <bean id="validator"
        class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <!-- hibernate校验器-->
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
        <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource" />
    </bean>
<!-- 校验错误信息配置文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <!-- 资源文件名-->
        <property name="basenames">   
            <list>    
            <value>classpath:CustomValidationMessages</value> 
            </list>   
        </property>
        <!-- 资源文件编码格式 -->
        <property name="fileEncodings" value="utf-8" />
        <!-- 对资源文件内容缓存时间,单位秒 -->
        <property name="cacheSeconds" value="120" />
    </bean>
    <!-- 全局异常处理器
    只要实现HandlerExceptionResolver接口就是全局异常处理器
     -->
    <bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean>
    
    <!-- 文件上传 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设置上传文件的最大尺寸为5MB -->
        <property name="maxUploadSize">
            <value>5242880</value>
        </property>
    </bean>
    
    <!--拦截器 -->
<mvc:interceptors>
    <!--多个拦截器,顺序执行 -->
    <!-- 登陆认证拦截器 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="cn.itcast.ssm.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <!-- /**表示所有url包括子url路径 -->
        <mvc:mapping path="/**"/>
        <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>    
</beans>
springmvc.xml

校验思路:

页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。

具体需求:

商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。

3.3      环境准备

hibernate的校验框架validation所需要jar包:

3.4      配置校验器(springmvc.xml)

<!-- 校验器 -->
    <bean id="validator"
        class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <!-- hibernate校验器-->
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
        <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource" />
    </bean>
<!-- 校验错误信息配置文件 -->
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <!-- 资源文件名-->
        <property name="basenames">   
            <list>    
            <value>classpath:CustomValidationMessages</value> 
            </list>   
        </property>
        <!-- 资源文件编码格式 -->
        <property name="fileEncodings" value="utf-8" />
        <!-- 对资源文件内容缓存时间,单位秒 -->
        <property name="cacheSeconds" value="120" />
    </bean>

3.5      校验器注入到处理器适配器中

<mvc:annotation-driven conversion-service="conversionService" validator="validator"></mvc:annotation-driven>

3.6      在pojo中添加校验规则

在ItemsCustom.java中添加校验规则:

3.7      CustomValidationMessages.properties

在CustomValidationMessages.properties配置校验错误信息:

 

3.8      捕获校验错误信息

//在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult bindingResult接收校验出错信息
//注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
@RequestMapping("/editItemsSubmit")
    public String editItemsSubmit(
            HttpServletRequest request,Integer id,@Validated ItemsCustom itemsCustom,BindingResult bindingResult) throws Exception {

3.9      在页面显示校验错误信息

在controller中将错误信息传到页面即可。

页面显示错误信息:

 

3.10      分组校验

ps:任意将几条校验规则可以分为不同组(不同组规则可以重复),组名为自定义定义不同接口的名字

3.10.1     需求

在pojo中定义校验规则,而pojo是被多个 controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验。

解决方法:

定义多个校验分组(其实是一个java接口),分组中定义有哪些规则
每个controller方法使用不同的校验分组

3.10.2     校验分组

3.10.3     在校验规则中添加分组

ps:验证规则上可以同时定义多个组

3.10.4     在controller方法使用指定分组的校验

 

4     数据回显

4.1      什么数据回显

提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。

ps:即表单的数据,提交后还在页面上(一般用于提交失败,方便重新修改)

4.2      pojo数据回显方法

1、springmvc默认对pojo数据进行回显。

1,pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写(ps:也就是说,跳转的页面自动从传入的数据中取值,但是页面的属性名称和传入的对象名称必须对应,也可以通过注解自定义名称)
2,使用@ModelAttribute指定pojo回显到页面在request中的key(方法中添加注解,自定义前端属性的名称)

前端页面:

 

2、@ModelAttribute还可以将方法的返回值传到页面

在商品查询列表页面,通过商品类型查询商品信息。

在controller中定义商品类型查询方法,最终将商品类型传到页面。

页面上可以得到itemTypes数据。

3、使用最简单方法,直接使用model存入,可以不用@ModelAttribute

 

4.3     简单类型数据回显

使用最简单方法使用model。

model.addAttribute("id", id);

ps:默认对象参数会被自动存入request域中,简单类型需要手动存入,model对象的数据会自动存入request域中。

 

posted @ 2018-06-13 00:26  假程序猿  阅读(2882)  评论(0)    收藏  举报