版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37546891/article/details/82726782
>> springmvc_mybatis 项目结构
>> springmvc_mybatis jar包
>> config
~ web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ......>
<display-name>springmvc_mybatis</display-name>
<!-- 加载spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- springmvc前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<!-- url-pattern 有3种配置方式:
1、*.action,访问以.action为结尾的地址,由DispatcherServlet进行解析;
2、斜杠/,所有访问的地址,都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析;
使用此种方式可以实现RESTful风格的url;
3、/*,这样配置不对,使用这种配置,最终要转发到一个jsp页面,
仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到Handler,会报错; -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!-- springmvc前端控制器:rest配置 -->
<servlet>
<servlet-name>springmvc_rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc_rest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 乱码过滤器:解决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>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
~ mybatis/SqlMapConfig.xml
<configuration>
<!-- 全局setting配置,根据需要添加 -->
<!-- 配置别名 :使用批量扫描别名-->
<typeAliases><package name="cn.itcast.ssm.po" /></typeAliases>
<!-- 加载映射文件mapper
由于使用spring和mybatis的整合包进行mapper扫描,这里就不需要配置了;
必须遵循:mapper.xml和mapper.java同名且在一个目录; -->
<!-- <mappers></mappers> -->
</configuration>
~ spring/applicationContext-xxx.xml 文件头
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
</beans>
~ spring/applicationContext-dao.xml
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 加载数据源:使用dbcp;class属性的值在dbcpjar包的org.apache.commons.dbcp2包中 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- <property name="maxActive" value="10" /> 貌似dbcp2弃用这个属性了 -->
<property name="maxIdle" value="5" />
</bean>
<!-- SqlSessionFactory:类在mybatis-spring整合包中 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"></property>
<!-- 加载数据源:数据库连接池 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定扫描的包名;如果扫描多个包,不能使用*,每个包中间使用逗号隔开; -->
<property name="basePackage" value="cn.itcast.ssm.mapper"></property>
<!-- sqlSessionFactoryBeanName如果使用sqlSessionFactory, 则加载数据库属性的配置文件失效,因为这个先执行 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
~ spring/applicationContext-service.xml
<!-- 商品管理的service -->
<bean id="itemsService" class="cn.itcast.ssm.service.impl.ItemsServiceImpl"></bean>
~ spring/applicationContext-transaction.xml
<!-- 事务管理器:对mybatis操作数据库事务控制,spring使用jdbc的事务控制类; -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源:dataSource在application-dao.xml文件中配置了 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/> <!-- 传播行为 -->
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- aop -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.ssm.service.impl.*.*(..))"/>
</aop:config>
~ spring/springmvc.xml
<!-- 批量扫描:可扫描controller、service、...... -->
<context:component-scan base-package="cn.itcast.ssm.controller"></context:component-scan>
<!-- 静态资源解析:js/css/img... -->
<mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
<mvc:resources location="/img/" mapping="/img/**"></mvc:resources>
<!-- 使用mvc:annotation-driven配置注解映射器和注解适配器配置; -->
<mvc:annotation-driven conversion-service="conversionService" validator="validator"></mvc:annotation-driven>
<!-- 自定义参数绑定:将Converter实现类注入到处理器适配器中 -->
<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"></property>
<!-- 指定校验使用的资源文件,若不能指定,则默认使用classpath下的ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource"></property>
</bean>
<!-- 校验错误信息配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 资源文件名 -->
<property name="basenames">
<list><value>classpath:CustomValidationMessages</value></list>
</property>
<property name="defaultEncoding" value="UTF-8"></property> <!-- 资源文件编码格式 -->
<property name="cacheSeconds" value="120"></property> <!-- 对资源文件内容缓存时间,单位秒 -->
</bean>
<!-- 全局异常处理器:只要实现HandlerExceptionResolver接口就是全局异常处理器 -->
<bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean>
<!-- 文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize"><value>5242880</value></property> <!-- 设置上传文件的最大尺寸为5MB -->
</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>
<!-- 视图解析器:解析jsp页面,默认使用jstl标签,classpath系需要有jstl的jar包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
~ CustomValidationMessages.properties
# 添加校验错误提示信息
items.name.length.error=请输入1-30个字符的商品名称
items.creametime.isNull=请输入商品的生产日期
~ db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=123456
~ log4j.properties
# 日志文件
log4j.rootLogger = debug,stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
>> src
~ ssm.controller/ItemsController.java
@Controller
// 为了对url进行分类管理,可以在这里定义根路径,最终访问url是根路径+子路径
// 比如:(1)商品列表:/items/queryItems.action
@RequestMapping("/items")
public class ItemsController {
@Autowired // 注入ItemsService
private ItemsService itemsService;
// 商品分类
// itemtypes 表示最终将方法返回值放在request中的可以,根据这个值在jsp页面取数据;
@ModelAttribute("itemtypes")
public Map<String,String> getItemTypes(){
Map<String,String> itemTypes = new HashMap<String,String>();
itemTypes.put("101", "数码");
itemTypes.put("102", "母婴");
return itemTypes;
}
// (1)商品查询
@RequestMapping("/queryItems")
public ModelAndView queryItems(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception {
// 调用service查找数据库,查询商品列表
List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo);
// 返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 相当于request的setAttribute,在jsp页面中通过itemsList取数据;
modelAndView.addObject("itemsList", itemsList);
// 指定视图:在springmvc.xml文件中的视图解析器中配置了jsp路径的前缀和后缀;
modelAndView.setViewName("items/queryItems");
return modelAndView;
}
// (2)商品信息修改页面显示
// method 以数组的方式设置限制http请求方法,可以post+get
// (7)简单类型参数绑定
// @RequestParam里面指定request传入参数名称和形参进行绑定;
// 通过request属性指定参数是否必须传入
// 通过defaultValue 可以设置默认值,若id参数没有传入,将默认值和形参绑定;
@RequestMapping(value = "/editItems", method = { RequestMethod.POST, RequestMethod.GET })
public ModelAndView editItems(@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("修改的商品信息不存在!");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("items", itemsCustom);// 这个items用于editItems.jsp页面获取itemsCustom的数据;
modelAndView.setViewName("items/editItems");
return modelAndView;
}
// (10)查询商品信息,输出json
// /itemsView/{id}里面的{id} 表示将这个位置的id参数 传到@PathVariable指定的名称中‘
@RequestMapping("/itemsView/{id}")
public @ResponseBody ItemsCustom itemsView(@PathVariable("id") Integer id) throws Exception{
// 调用service查询商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(id);
return itemsCustom;
}
// (3)商品信息修改提交
// (9)在需要校验的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, Date creametime,
@ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom,
BindingResult bindingResult,
MultipartFile items_pic // 从editItems.jsp页面 接收上传的 商品图片
) throws Exception {
// 获取校验错误信息
if(bindingResult.hasErrors()){
// 输出错误信息
List<ObjectError> allErrors = bindingResult.getAllErrors();
// for (ObjectError objectError : allErrors) {
// System.out.println(objectError.getDefaultMessage());
// }
model.addAttribute("allErrors", allErrors); // 将错误信息传到页面
model.addAttribute("items", itemsCustom); // 可以直接使用model将提交的pojo 回显到页面;
return "items/editItems"; // 出错重新到商品修改页面
}
String originalFilename = items_pic.getOriginalFilename(); // 原始名称
// 上传图片:原始图片为空说明没有在页面上传图片,则不进行下面上传图片的操作;
if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){
String pic_path = "E:\\WorkSpace\\pictureTemp\\"; // 存储图片的物理路径:将图片存储到这里;
String newFileName = UUID.randomUUID() // 新图片的名称
+ originalFilename.substring(originalFilename.lastIndexOf("."));
File newFile = new File(pic_path+newFileName); // 新图片存储路径+存储名称
items_pic.transferTo(newFile); // 将内存中的数据写入磁盘
itemsCustom.setPic(newFileName); // 将新图片写到ItemsCustom中
}
itemsService.updateItems(id, itemsCustom); //调用service更新商品信息,页面需要将商品信息传到此方法;
// ......功能未实现,返回success页面;
// ModelAndView modelAndView = new ModelAndView();
// modelAndView.setViewName("success");
return "success";
}
// (4)controller方法返回值返回string:返回逻辑视图名
@RequestMapping(value = "/editItems1", method = { RequestMethod.POST, RequestMethod.GET })
public String editItems1(Model model) throws Exception {
ItemsCustom itemsCustom = itemsService.findItemsById(2);
// 通过形参中的model将model数据传到页面,相当于ModelAndView的addObject方法;
model.addAttribute("itemsCustom", itemsCustom);
return "items/editItems";
}
// (5)controller方法返回值返回string:redirect重定向
// (6)controller方法返回值返回string:forward页面转发
@RequestMapping("/editItemsSubmit1")
public String editItemsSubmit1() throws Exception {
// return "redirect:queryItems.action"; // (5)重定向到商品查询列表
return "forward:queryItems.action"; // (6)页面转发到商品查询列表
}
// (7)批量删除商品信息
@RequestMapping("/deleteItems")
public String deleteItems(Integer[] items_id) throws Exception {
// 调用service批量删除商品......其他操作...
return "success";
}
// (8)批量修改商品页面,将商品信息查询出来,在页面中可以编辑商品信息
@RequestMapping("/editItemsQuery")
public ModelAndView editItemsQuery(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception {
List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("items/editItemsQuery");
return modelAndView;
}
// (8)批量修改商品提交
// 通过ItemsQueryVo接收批量提交的商品信息,将商品信息存储到itemsQueryVo中的itemsList属性中;
@RequestMapping("editItemsAllSubmit")
public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo) throws Exception {
return "success";
}
}
~ ssm.controller/JsonTest.java
@Controller
public class JsonTest { // json交互测试
// 请求json串(商品信息),输出json串(商品信息)
// @RequestBody 将请求的商品信息的json串转成itemsCustom对象;
// @ResponseBody 将 itemsCustom 转成 json 串输出;
@RequestMapping("/requestJson")
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){
// @ResponseBody 将 itemsCustom 转成 json 串输出;
return itemsCustom;
}
// 请求key/value,输出json
@RequestMapping("/responseJson")
public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){
// @ResponseBody 将 itemsCustom 转成 json 串输出;
return itemsCustom;
}
}
~ ssm.controller/LoginController.java
@Controller
public class LoginController {
// 登录方法
@RequestMapping("/login")
public String login(HttpSession session,String username,String password) throws Exception{
// 调用service进行用户身份验证
// ...
// 在session中保存用户身份信息
session.setAttribute("username", username);
// 重定向到商品列表查询页面
return "redirect:/items/queryItems.action";
}
// 退出方法
@RequestMapping("/logout")
public String logout(HttpSession session) throws Exception{
// 清除session,使session状态过期
session.invalidate();
// 重定向到商品列表查询页面
return "redirect:/items/queryItems.action";
}
}
~ ssm.controller.converter/CustomDateConverter.java
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) {
e.printStackTrace();
}
return null; // 若参数绑定失败,则返回null;
}
}
~ ssm.controller.validation/ValidGroup1/2.java
public interface ValidGroup1 {
// 接口中不需要定义任何方法,仅是对不同的校验规则进行分组
// 此分组只校验商品名称的长度
}
~ ssm.exception/CustomException.java
// 系统自定义异常类,针对预期的异常,需要在程序中抛出此类的异常;
public class CustomException extends Exception{
public String message; // 异常信息
public CustomException(String message){
super(message);
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
~ ssm.exception/CustomExceptionResolver.java
// 全局异常处理器
public class CustomExceptionResolver implements HandlerExceptionResolver {
// ex:系统抛出的异常
// Handler:处理器适配器要执行的Handler对象(只有method)
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response
, Object handler, Exception ex) {
// 解析出异常类型
// 如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示;
// String message = null;
// if(ex instanceof CustomException){
// message = ((CustomException)ex).getMessage();
// }else{// 如果该异常类型不是系统自定义的异常,则构造一个自定义的异常类型(信息为“未知错误”)
// message = "未知错误";
// }
// 上边代码变为:
CustomException customException = null;
if(ex instanceof CustomException){
customException = (CustomException)ex;
}else{
customException = new CustomException("未知错误!");
}
String message = customException.getMessage(); // 错误信息
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", message); // 将错误信息传到页面
modelAndView.setViewName("error"); // 指向错误页面
return modelAndView;
}
}
~ ssm.interceptor/HandlerInterceptor1/2.java
// 测试拦截器1
public class HandlerInterceptor1 implements HandlerInterceptor {
// 进入Handler方法之前执行,用于身份认证、身份授权等;
// 比如:身份认证,若认证不通过,表示当前用户没有登录,需要此方法拦截,不再向下执行;
@Override
public boolean preHandle(HttpServletRequest request
, HttpServletResponse response, Object handler)throws Exception {
System.out.println("HandlerInterceptor1...preHandle");
// return false表示拦截,不向下执行;
// return true表示放行;
return true;
}
// 进入Handler方法之后,返回ModelAndView之前执行;
// 应用场景从ModelAndView出发:将共用的模型数据(比如菜单导航栏)在这里传到视图,也可以在这里同一指定视图;
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response
, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
System.out.println("HandlerInterceptor1...postHandle");
}
// 执行Handler完成之后,执行此方法;
// 应用场景:统一异常处理、统一日志处理;
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response
, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
~ ssm.interceptor/LoginInterceptor
// 登录认证的拦截器
public class LoginInterceptor implements HandlerInterceptor {
// 进入Handler方法之前执行,用于身份认证、身份授权等;
// 比如:身份认证,若认证不通过,表示当前用户没有登录,需要此方法拦截,不再向下执行;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response
, Object handler)throws Exception {
// 获取请求的url
String url = request.getRequestURI();
// 判断url是否是公开地址(实际使用时将公开地址配置在配置文件中)
// 这里假设公开地址是登录提交的地址
if(url.indexOf("login.action")>=0){
return true; // 如果request域获取到的url是login的url,说明是公开的登录地址,放行;
}
HttpSession session = request.getSession(); // 判断session:先从request域中获取到session
String username = (String) session.getAttribute("username"); // 从session中取出用户身份信息
if(username != null){
return true; // 如果从session中获取的username不为空,说明身份存在,放行;
}
// 执行到这里说明用户身份需要认证,跳转到登录页面;
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
// 进入Handler方法之后,返回ModelAndView之前执行;
// 应用场景从ModelAndView出发:将共用的模型数据(比如菜单导航栏)在这里传到视图,也可以在这里同一指定视图;
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response
, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
// 执行Handler完成之后,执行此方法;
// 应用场景:统一异常处理、统一日志处理;
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response
, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
~ ssm.mapper/ItemsMapperCustom .java
public interface ItemsMapperCustom {
//查询商品列表
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;
}
~ ssm.mapper/ItemsMapperCustom.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="cn.itcast.ssm.mapper.ItemsMapperCustom">
<!-- 定义商品查询的sql片段,就是商品查询条件 -->
<sql id="query_items_where">
<!-- 使用动态sql,通过if判断,满足条件进行拼接; -->
<!-- 查询条件通过ItemsQueryVo包装对象中的itemsCustom属性进行传递; -->
<if test="itemsCustom!=null">
<if test="itemsCustom.name!=null and itemsCustom.name!=''">
items.name LIKE '%${itemsCustom.name}%'
</if>
</if>
</sql>
<!-- 商品列表查询 -->
<!-- parameterType传入包装对象(包装了查询条件)
resultType建议使用扩展对象 -->
<select id="findItemsList" parameterType="cn.itcast.ssm.po.ItemsQueryVo"
resultType="cn.itcast.ssm.po.ItemsCustom">
SELECT items.* FROM items
<where><include refid="query_items_where"></include></where>
</select>
</mapper>
~ ssm.po/Items.java
public class Items {
private Integer id;
// 校验名称在1-30个字符之间;message是提示校验出错显示的信息;
@Size(min = 1, max = 30, message = "{items.name.length.error}",groups={ValidGroup1.class})
private String name;
@NotNull(message = "{items.creametime.isNull}")
private Date creametime;
private Float price;
private String pic;
private String detail;
// set/get()......
}
~ ssm.po/ItemsCustom.java
// 商品信息的扩展类
public class ItemsCustom extends Items{
// 添加商品信息的扩展信息
}
~ ssm.po/ItemsQueryVo.java
public class ItemsQueryVo { // 商品包装对象类
private Items items; // 商品信息
private ItemsCustom itemsCustom; // 为了系统可扩展性,对原始生成的po进行扩展
private List<ItemsCustom> itemsList; // 批量商品信息
// set/get()......
}
~ ssm.service/ItemsService.java
public interface ItemsService { // 商品管理service
// (2)根据id查询商品信息
public ItemsCustom findItemsById(Integer id) throws Exception;
// (3)修改商品信息
public void updateItems(Integer id,ItemsCustom itemsCustom) throws Exception;
// (1)查询商品列表
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;
}
~ ssm.service/ItemsServiceImpl.java
public class ItemsServiceImpl implements ItemsService { // 商品管理
@Autowired // 注入ItemsMapperCustom
private ItemsMapperCustom itemsMapperCustom;
@Autowired // 单表查询商品信息需要注入ItemsMapper
private ItemsMapper itemsMapper;
@Override // (2)根据id查询商品信息
public ItemsCustom findItemsById(Integer id) throws Exception {
// 这里使用itemsMapper的方法查询Items单表的信息;
Items items = itemsMapper.selectByPrimaryKey(id);
if(items==null){
throw new CustomException("修改的商品信息不存在!!");
}
// 中间对商品信息进行业务处理......实际开发中会有很多操作;
ItemsCustom itemsCustom = null;
// 创建ItemsCustom扩展类对象,再将items的属性值拷贝到itemsCustom对象中;
if(items != null){
itemsCustom = new ItemsCustom();
BeanUtils.copyProperties(items, itemsCustom);
}
return itemsCustom;
}
@Override // (3)修改商品信息
public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception {
// 添加业务校验,通常在service接口对关键参数进行校验;
// 校验id是否为空,若为空则抛出异常;
// 更新商品信息使用下面的方法,根据id更新Items表中的所有字段,包括大文本字段;
// 下面这个方法updateByPrimaryKeyWithBLOBs,要求必须传入id;
itemsCustom.setId(id); // 宁可重复set,也要尽可能避免id为空;
itemsMapper.updateByPrimaryKeyWithBLOBs(itemsCustom);
}
@Override // (1)查询商品列表
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception {
// 通过ItemsMapperCustom查询数据库
return itemsMapperCustom.findItemsList(itemsQueryVo);
}
}
>> jsp
~ jsp/items/editItems.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 && allErrors.size()>0 }">
<c:forEach items="${allErrors }" var="error">
${error.defaultMessage }<br/>
</c:forEach>
</c:if>
<form id="itemForm" method="post" enctype="multipart/form-data"
action="${pageContext.request.contextPath}/items/editItemsSubmit.action">
<input type="hidden" name="id" value="${items.id}" >
修改商品信息:
<table width="100%" border="1">
<tr><td>商品名称</td>
<td><input type="text" name="name" value="${items.name}" /></td>
</tr>
<tr><td>商品价格</td>
<td><input type="text" name="price" value="${items.price}" /></td>
</tr>
<tr><td>商品生产日期</td>
<td><input type="text" name="creametime"
value="<fmt:formatDate value="${items.creametime}"
pattern="yyyy-MM-dd HH:mm:ss" />" /></td>
</tr>
<tr><td>商品图片</td>
<td>
<c:if test="${items.pic!=null }">
<img src="/pic/${items.pic }" width="100" height="100" /><br/>
</c:if>
<input type="file" name="items_pic" />
</td>
</tr>
<tr><td>商品简介</td>
<td><textarea rows="3" cols="30" name="detail">${items.detail}</textarea></td>
</tr>
<tr><td colspan="2" align="center"><input type="submit" value="提交" /></td></tr>
</table>
</form>
</body>
</html>
~ jsp/items/editItemsQuery.jsp
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>查询商品列表</title>
<script type="text/javascript">
function editItemsAllSubmit() { // 提交form
document.itemsForm.action="${pageContext.request.contextPath}/items/editItemsAllSubmit.action";
document.itemsForm.submit();
}
function queryItems() { // 提交form
document.itemsForm.action="${pageContext.request.contextPath}/items/queryItems.action";
document.itemsForm.submit();
}
</script>
</head>
<body>
<form name="itemsForm" method="post"
action="${pageContext.request.contextPath}/items/queryItems.action">
查询条件:
<table width="100%" border="1">
<tr>
<td>商品名称<input name="itemsCustom.name" /></td>
<td><input type="button" value="查询" onclick="queryItems()" /></td>
<td><input type="button" value="批量修改提交" onclick="editItemsAllSubmit()" /></td>
</tr>
</table>
商品列表:
<table width="100%" border="1">
<tr>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList}" var="item" varStatus="status">
<tr>
<td><input name="itemsList[${status.index }].name" value="${item.name}"></td>
<td><input name="itemsList[${status.index }].price" value="${item.price}"></td>
<td><input name="itemsList[${status.index }].creametime"
value="<fmt:formatDate value="${item.creametime}"
pattern="yyyy-MM-dd HH:mm:ss" />"></td>
<td><input name="itemsList[${status.index }].detail" value="${item.detail}"></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
~ jsp/items/queryItems.jsp
<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" method="post"
action="${pageContext.request.contextPath}/items/queryItems.action">
查询条件:
<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()" /></td>
<td><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.creametime}" 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>
~ jsp/error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!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>
${message }
</body>
</html>
~ jsp/login.jsp
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>系统登录</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login.action" method="post">
用户账号:<input type="text" name="username"><br/>
用户密码:<input type="password" name="password"><br/>
<input type="submit" value="登录" />
</form>
</body>
</html>
~ jsp/success.jsp
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>成功</title>
</head>
<body>
操作成功!
</body>
</html>
~ jsonTest.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!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>json交互测试</title>
<script type="text/javascript" // 引入jQuery的js包
src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js"></script>
<script type="text/javascript">
function requestJson(){ // 请求json,输出json
$.ajax({
type:'post',
url:'${pageContext.request.contextPath}/requestJson.action',
contentType:'application/json;charset=utf-8',
data:'{"name":"手机","price":999}', // 数据格式是json串;
success:function(data){ // 返回json结果;
alert(data.name);
}
});
}
function responseJson(){ // 请求key/value,输出json
$.ajax({
type:'post',
url:'${pageContext.request.contextPath}/responseJson.action',
// 请求是key/value,这里不需要指定contentType,因为默认就是key/value类型;
// contentType:'application/json;charset=utf-8', //
data:'name=手机&price=998', // 数据格式是key/value串;
success:function(data){
alert(data.price);
}
});
}
</script>
</head>
<body>
<input type="button" onclick="requestJson()" value="请求json,输出json" />
<input type="button" onclick="responseJson()" value="请求key/value,输出json" />
</body>
</html>