├── README.md ├── course ├── Mybatis.md ├── SpringMVC.md └── 上课笔记 │ ├── mybatis入门教程(一)(超简单).docx │ ├── mybatis入门教程(二)(超简单).docx │ ├── springmvc第一天课堂笔记.docx │ └── springmvc第二天课堂笔记.docx ├── imgs ├── logo.png ├── logo2.png └── mybatis-superbird-small.png ├── mybatis ├── 01 对原生JDBC程序中的问题总结.md ├── 02 mybatis概述.md ├── 03 入门程序一.md ├── 03 入门程序二.md ├── 04 开发dao方法.md ├── 05 配置文件.md ├── 06 输入映射.md ├── 07 输出映射.md ├── 08 动态sql.md ├── 09 订单商品数据模型分析.md ├── 10 一对一查询.md ├── 11 一对多查询.md ├── 12 多对多查询.md ├── 13 延迟加载.md ├── 14 查询缓存之一级缓存.md ├── 15 查询缓存之二级缓存.md ├── 16 mybatis整合ehcache.md ├── 17 spring和mybatis整合.md └── 18 mybatis逆向工程.md ├── spring ├── 1 Spring入门.md ├── 2 Spring的bean管理.md ├── 3 AOP思想.md ├── 4 Spring的jdbcTemplate.md ├── 5 ssh整合.md └── assets │ ├── aop_1.png │ ├── aop_2.png │ ├── aop_3.png │ ├── aop术语.png │ ├── ioc原理.png │ ├── ioc过程1.png │ ├── java属性注入.png │ └── ssh_1.png └── springmvc ├── 00 课程安排.md ├── 01 框架原理和入门配置.md ├── 02 非注解的处理器映射器和适配器.md ├── 03 注解的处理器映射器和适配器.md ├── 04 前端控制器.md ├── 05 入门程序小结.md ├── 06 springmvc整合mybatis-mvn构建.md ├── 07 springmvc整合mybatis之mapper.md ├── 08 springmvc整合mybatis之service.md ├── 09 springmvc整合mybatis之controller.md ├── 10 springmvc注解开发之商品修改功能.md ├── 11 springmvc注解开发之简单参数绑定.md ├── 12 springmvc注解开发之包装类型参数绑定.md ├── 13 springmvc注解开发之集合类型参数绑定.md ├── 14 springmvc校验.md ├── 15 数据回显.md ├── 1533949939501.png ├── 16 异常处理器.md ├── 17 上传图片.md ├── 18 json数据交互.md ├── 19 RESTful支持.md ├── 20 拦截器.md ├── 21 springmvc整合mybatis遇到的问题及解决小结.md ├── 22 springmvc开发小结.md └── assets ├── 1536050065921.png ├── 1547948320307.png ├── 1547948554726.png ├── 1547948611496.png ├── 1547948632377.png ├── 1547948685468.png ├── 1547948785389.png ├── 1547948785390.png ├── Spring-mvc-framework.png ├── spring-overview.png └── springmvc_核心架构图.jpg /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | ## 前言 6 | 7 | 本仓库是 「黑马程序员SpringMVC+MyBatis」学习课程笔记和代码记录 8 | 9 | 10 | 11 | SpringMVC文档: 12 | 13 | - [Spring MVC 4.2.4 RELEASE 中文文档完整翻译稿](https://github.com/linesh-simplicity/translation-spring-mvc-4-documentation) 14 | - [一版有追求的 Spring MVC 中文文档翻译来袭~ - V2EX](https://www.v2ex.com/t/288717) 15 | 16 | 17 | 18 | ## 学习目录 19 | 20 | ### MyBatis 21 | - [01 对原生JDBC程序中的问题总结](mybatis/01%20对原生JDBC程序中的问题总结.md) 22 | - [02 mybatis概述](mybatis/02%20mybatis概述.md) 23 | - [03 入门程序一](mybatis/03%20入门程序一.md) 24 | - [03 入门程序二](mybatis/03%20入门程序二.md) 25 | - [04 开发dao方法](mybatis/04%20开发dao方法.md) 26 | - [05 配置文件](mybatis/05%20配置文件.md) 27 | - [06 输入映射](mybatis/06%20输入映射.md) 28 | - [07 输出映射](mybatis/07%20输出映射.md) 29 | - [08 动态sql](mybatis/08%20动态sql.md) 30 | - [09 订单商品数据模型分析](mybatis/09%20订单商品数据模型分析.md) 31 | - [10 一对一查询](mybatis/10%20一对一查询.md) 32 | - [11 一对多查询](mybatis/11%20一对多查询.md) 33 | - [12 多对多查询](mybatis/12%20多对多查询.md) 34 | - [13 延迟加载](mybatis/13%20延迟加载.md) 35 | - [14 查询缓存之一级缓存](mybatis/14%20查询缓存之一级缓存.md) 36 | - [15 查询缓存之二级缓存](mybatis/15%20查询缓存之二级缓存.md) 37 | - [16 mybatis整合ehcache](mybatis/16%20mybatis整合ehcache.md) 38 | - [17 spring和mybatis整合](mybatis/17%20spring和mybatis整合.md) 39 | - [18 mybatis逆向工程](mybatis/18%20mybatis逆向工程.md) 40 | 41 | 42 | 43 | 44 | #### Spring MVC 45 | - [01 框架原理和入门配置](springmvc/01%20框架原理和入门配置.md) 46 | - [02 非注解的处理器映射器和适配器](springmvc/02%20非注解的处理器映射器和适配器.md) 47 | - [03 注解的处理器映射器和适配器](springmvc/03%20注解的处理器映射器和适配器.md) 48 | - [04 前端控制器](springmvc/04%20前端控制器.md) 49 | - [05 入门程序小结](springmvc/05%20入门程序小结.md) 50 | - [06 springmvc整合mybatis IDEA中通过maven构建](springmvc/06%20springmvc整合mybatis-mvn构建.md) 51 | - [07 springmvc整合mybatis之mapper](springmvc/07%20springmvc整合mybatis之mapper.md) 52 | - [08 springmvc整合mybatis之service](springmvc/08%20springmvc整合mybatis之service.md) 53 | - [09 springmvc整合mybatis之controller](springmvc/09%20springmvc整合mybatis之controller.md) 54 | - [10 springmvc注解开发之商品修改功能](springmvc/10%20springmvc注解开发之商品修改功能.md) 55 | - [11 springmvc注解开发之简单参数绑定](springmvc/11%20springmvc注解开发之简单参数绑定.md) 56 | - [12 springmvc注解开发之包装类型参数绑定](springmvc/12%20springmvc注解开发之包装类型参数绑定.md) 57 | - [13 springmvc注解开发之集合类型参数绑定](springmvc/13%20springmvc注解开发之集合类型参数绑定.md) 58 | - [14 springmvc校验](springmvc/14%20springmvc校验.md) 59 | - [15 数据回显](springmvc/15%20数据回显.md) 60 | - [16 异常处理器](springmvc/16%20异常处理器.md) 61 | - [17 上传图片](springmvc/17%20上传图片.md) 62 | - [18 json数据交互](springmvc/18%20json数据交互.md) 63 | - [19 RESTful支持](springmvc/19%20RESTful支持.md) 64 | - [20 拦截器](springmvc/20%20拦截器.md) 65 | 66 | 67 | 68 | ## 课程链接 69 | 70 | 哔哩哔哩在线: 71 | 72 | - [SpringMvc+Mybatis由浅入深全套视频教程(完整版)](https://www.bilibili.com/video/av27573870) 73 | 74 | 75 | 76 | 77 | 78 | ## 这里有更多后台技术栈 79 | 80 | [frank-lam/fullstack-tutorial 后台技术栈/架构师成长之路,春招/秋招/校招/面试。](https://github.com/frank-lam/fullstack-tutorial) 81 | 82 | 83 | 84 | ## 关于作者 :boy: 85 | 86 |
87 |

88 | 在颠覆世界的同时,也要好好关照自己。 89 |

90 | QQ群 91 | CSDN 92 | 邮箱 93 | QQ群 94 |

95 |

96 | from zero to hero. 97 |

98 |
99 |

-------------------------------------------------------------------------------- /course/Mybatis.md: -------------------------------------------------------------------------------- 1 | ## 黑马程序员 Mybatis 学习记录 2 | 3 | 时长:10小时23分钟 4 | 5 | | 章节 | 记录 | 6 | | ------------------------------------------------- | ---- | 7 | | **day-01** | | 8 | | 01 mybatis第一天课程安排 | | 9 | | 02 原生态jdbc编程中的问题总结 | | 10 | | 03 mybatis框架原理 | | 11 | | 04 mybatis入门程序-工程结构 | | 12 | | 05 mybatis入门程序-根据id查询用户-映射文件 | | 13 | | 06 mybatis入门程序-根据id查询用户-程序代码 | | 14 | | 07 mybatis入门程序-根据id查询用户-程序调试 | | 15 | | 08 mybatis入门程序-根据名称查询用户 | | 16 | | 09 mybatis入门程序-查询用户小结 | | 17 | | 10 mybatis入门程序-添加用户 | | 18 | | 11 mybatis入门程序-添加用户-主键返回 | | 19 | | 12 mybatis入门程序-删除用户和更新用户 | | 20 | | 13 mybatis入门程序-小结 | | 21 | | 14 mybatis和hibernate的本质区别和应用场景 | | 22 | | 15 mybatis开发dao方法-sqlSession应用场合 | | 23 | | 16 mybatis开发dao方法-原始dao开发方法 | | 24 | | 17 mybatis开发dao方法-原始dao开发方法-问题总结 | | 25 | | 18 mybatis开发dao方法-mapper代理开发方法-开发规范 | | 26 | | 19 mybatis开发dao方法-mapper代理开发方法-实现 | | 27 | | 20 SqlMapConfig-properties定义 | | 28 | | 21 SqlMapConfig-settings | | 29 | | 22 SqlMapConfig-别名定义 | | 30 | | 23 SqlMapConfig-类型处理器 | | 31 | | 24 SqlMapConfig-mapper加载 | | 32 | | 25 输入映射-pojo包装类型-定义pojo包装类型 | | 33 | | 26 输入映射-pojo包装类型-实现 | | 34 | | 27 输出映射-resultType | | 35 | | 28 输出映射-resultMap | | 36 | | 29 动态sql-if判断 | | 37 | | 30 动态sql-sql片段 | | 38 | | 31 动态sql-foreach | | 39 | | **day-02** | | 40 | | 01 第一天课程复习及第二天课程安排 | | 41 | | 02 订单商品数据模型-分析思路 | | 42 | | 03 订单商品数据模型-分析 | | 43 | | 04 高级映射-一对一查询-使用resultType | | 44 | | 05 高级映射-一对一查询-使用resultMap | | 45 | | 06 高级映射-一对多查询 | | 46 | | 07 高级映射-多对多查询 | | 47 | | 08 高级映射-resultMap总结 | | 48 | | 09 高级映射-延迟加载 | | 49 | | 10 查询缓存-一级缓存原理 | | 50 | | 11 查询缓存-一级缓存测试 | | 51 | | 12 查询缓存-一级缓存实际应用 | | 52 | | 13 查询缓存-二级缓存-二级缓存测试 | | 53 | | 14 查询缓存-二级缓存-整合ehcache | | 54 | | 15 查询缓存-二级缓存-应用场景和局限性 | | 55 | | 16 mybatis和spring整合-sqlSessionFactory配置 | | 56 | | 17 mybatis和spring整合-原始dao开发 | | 57 | | 18 mybatis和spring整合-mapper代理开发 | | 58 | | 19 mybatis逆向工程自动生成代码 | | -------------------------------------------------------------------------------- /course/SpringMVC.md: -------------------------------------------------------------------------------- 1 | ## 黑马程序员 SpringMVC 学习记录 2 | 3 | 时长:10小时04分钟 4 | 5 | 6 | | 章节 | 记录 | 7 | | ----------------------------------------------------------- | ---- | 8 | | **day01** | | 9 | | 01 spring第一天课程安排 | | 10 | | 02 springmvc架构-mvc设计模式在bs系统下的应用 | | 11 | | 03 springmvc架构-springmvc架构原理分析 | | 12 | | 04 springmvc入门程序-环境搭建 | | 13 | | 05 springmvc入门程序-前端控制器配置 | | 14 | | 06 springmvc入门程序-Handler编写调试 | | 15 | | 07 springmvc入门程序-非注解映射器和适配器 | | 16 | | 08 springmvc入门程序-注解映射器和适配器配置 | | 17 | | 09 springmvc入门程序-注解映射器和适配器 | | 18 | | 10 springmvc入门程序-前端控制器源代码分析 | | 19 | | 11 springmvc入门程序-视图解析器配置前缀和后缀 | | 20 | | 12 springmvc和mybaits整合-整合思路 | | 21 | | 13 springmvc和mybaits整合-工程结构 | | 22 | | 14 springmvc和mybaits整合-商品查询mapper | | 23 | | 15 springmvc和mybaits整合-商品查询service | | 24 | | 16 springmvc和mybaits整合-商品查询controller | | 25 | | 17 springmvc和mybaits整合-商品查询调试 | | 26 | | 18 springmvc注解开发-商品修改功能分析 | | 27 | | 19 springmvc注解开发-商品修改功能开发service | | 28 | | 20 springmvc注解开发-商品修改功能开发controller | | 29 | | 21 springmvc注解开发-RequestMapping注解 | | 30 | | 22 springmvc注解开发-controller方法返回值 | | 31 | | 23 springmvc注解开发-springmvc参数绑定过程 | | 32 | | 24 springmvc注解开发-springmvc参数绑定-简单类型绑定 | | 33 | | 25 springmvc注解开发-springmvc参数绑定-pojo绑定 | | 34 | | 26 springmvc注解开发-springmvc参数绑定-自定义参数绑定 | | 35 | | 27 springmvc和struts2的区别 | | 36 | | | | 37 | | **day02** | | 38 | | 01 springmvc第一天课程复习 | | 39 | | 02 springmvc注解开发-springmvc参数绑定-包装类型pojo参数绑定 | | 40 | | 03 springmvc注解开发-springmvc参数绑定-数组 | | 41 | | 04 springmvc注解开发-springmvc参数绑定-list | | 42 | | 05 springmvc注解开发-validation校验-商品修改校验 | | 43 | | 06 springmvc注解开发-validation校验-分组校验 | | 44 | | 07 springmvc注解开发-数据回显 | | 45 | | 08 springmvc异常处理-全局异常处理器开发 | | 46 | | 09 springmvc异常处理-抛出异常 | | 47 | | 10 springmvc上传图片 | | 48 | | 11 springmvc实现json交互-requestBody和responseBody | | 49 | | 12 springmvc实现json交互-准备环境 | | 50 | | 13 springmvc实现json交互-json交互测试 | | 51 | | 14 springmvc对RESTful支持 | | 52 | | 15 springmvc拦截器-定义和配置 | | 53 | | 16 springmvc拦截器-多拦截器测试 | | 54 | | 17 springmvc拦截器-实现登陆认证 | | 55 | -------------------------------------------------------------------------------- /course/上课笔记/mybatis入门教程(一)(超简单).docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/course/上课笔记/mybatis入门教程(一)(超简单).docx -------------------------------------------------------------------------------- /course/上课笔记/mybatis入门教程(二)(超简单).docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/course/上课笔记/mybatis入门教程(二)(超简单).docx -------------------------------------------------------------------------------- /course/上课笔记/springmvc第一天课堂笔记.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/course/上课笔记/springmvc第一天课堂笔记.docx -------------------------------------------------------------------------------- /course/上课笔记/springmvc第二天课堂笔记.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/course/上课笔记/springmvc第二天课堂笔记.docx -------------------------------------------------------------------------------- /imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/imgs/logo.png -------------------------------------------------------------------------------- /imgs/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/imgs/logo2.png -------------------------------------------------------------------------------- /imgs/mybatis-superbird-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/imgs/mybatis-superbird-small.png -------------------------------------------------------------------------------- /mybatis/01 对原生JDBC程序中的问题总结.md: -------------------------------------------------------------------------------- 1 | # 01 对原生jdbc程序中的问题总结 2 | 3 | 标签:mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [jdbc编程步骤](#jdbc编程步骤) 10 | - [问题总结](#问题总结) 11 | - [参考代码](#参考代码) 12 | 13 | 14 | 15 | --- 16 | 17 | 本文总结jdbc编程的一般步骤,总结这样编程存在的问题,并附上典型地jdbc示例demo 18 | 19 | 20 | ## jdbc编程步骤 21 | 22 | 1. 加载数据库驱动 23 | 2. 创建并获取数据库链接 24 | 3. 创建jdbc statement对象 25 | 4. 设置sql语句 26 | 5. 设置sql语句中的参数(使用preparedStatement) 27 | 6. 通过statement执行sql并获取结果 28 | 7. 对sql执行结果进行解析处理 29 | 8. 释放资源(resultSet、preparedstatement、connection) 30 | 31 | 32 | ## 问题总结 33 | 34 | 1. 数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。 35 | 36 |   **设想:**使用数据库连接池管理数据库连接。 37 | 38 | 2. 将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护。 39 | 40 |   **设想:**将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。 41 | 42 | 3. 向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。 43 | 44 |   **设想:**将sql语句及占位符号和参数全部配置在xml中。 45 | 46 | 4. 从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。 47 | 48 |   **设想:**将查询的结果集,自动映射成java对象。 49 | 50 | 51 | 52 | 53 | 54 | ## 参考代码 55 | 56 | ```java 57 | package com.iot.mybatis.jdbc; 58 | 59 | //import java.sql.*; 60 | import java.sql.Connection; 61 | import java.sql.DriverManager; 62 | import java.sql.PreparedStatement; 63 | import java.sql.ResultSet; 64 | import java.sql.SQLException; 65 | 66 | /** 67 | * Created by Administrator on 2016/2/21. 68 | */ 69 | public class JdbcTest { 70 | public static void main(String[] args) { 71 | //数据库连接 72 | Connection connection = null; 73 | //预编译的Statement,使用预编译的Statement提高数据库性能 74 | PreparedStatement preparedStatement = null; 75 | //结果集 76 | ResultSet resultSet = null; 77 | 78 | try { 79 | //加载数据库驱动 80 | Class.forName("com.mysql.jdbc.Driver"); 81 | 82 | //通过驱动管理类获取数据库链接 83 | connection = DriverManager.getConnection("jdbc:mysql://120.25.162.238:3306/mybatis001?characterEncoding=utf-8", "root", "123"); 84 | //定义sql语句 ?表示占位符 85 | String sql = "select * from user where username = ?"; 86 | //获取预处理statement 87 | preparedStatement = connection.prepareStatement(sql); 88 | //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 89 | preparedStatement.setString(1, "王五"); 90 | //向数据库发出sql执行查询,查询出结果集 91 | resultSet = preparedStatement.executeQuery(); 92 | //遍历查询结果集 93 | while(resultSet.next()){ 94 | System.out.println(resultSet.getString("id")+" "+resultSet.getString("username")); 95 | } 96 | } catch (Exception e) { 97 | e.printStackTrace(); 98 | }finally{ 99 | //释放资源 100 | if(resultSet!=null){ 101 | try { 102 | resultSet.close(); 103 | } catch (SQLException e) { 104 | // TODO Auto-generated catch block 105 | e.printStackTrace(); 106 | } 107 | } 108 | if(preparedStatement!=null){ 109 | try { 110 | preparedStatement.close(); 111 | } catch (SQLException e) { 112 | // TODO Auto-generated catch block 113 | e.printStackTrace(); 114 | } 115 | } 116 | if(connection!=null){ 117 | try { 118 | connection.close(); 119 | } catch (SQLException e) { 120 | // TODO Auto-generated catch block 121 | e.printStackTrace(); 122 | } 123 | } 124 | 125 | } 126 | 127 | } 128 | 129 | } 130 | 131 | ``` 132 | -------------------------------------------------------------------------------- /mybatis/02 mybatis概述.md: -------------------------------------------------------------------------------- 1 | # 02 mybatis概述 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [mybatis 介绍](#mybatis-介绍) 10 | - [框架原理](#框架原理) 11 | - [mybatis框架执行过程](#mybatis框架执行过程) 12 | - [mybatis开发dao的方法](#mybatis开发dao的方法) 13 | - [输入映射和输出映射](#输入映射和输出映射) 14 | - [动态sql](#动态sql) 15 | 16 | 17 | 18 | --- 19 | 20 | 21 | 本文对mybatis做一个简单介绍,包括框架原理,执行过程,开发方法,输入输出映射以及动态sql,我会在后续的系列文章中一一详细说明 22 | 23 | ## mybatis 介绍 24 | 25 | mybatis是一个持久层的框架,是apache下的顶级项目。 26 | 27 | mybatis托管到goolecode下,再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)。 28 | 29 | mybatis让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。 30 | 31 | mybatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射) 32 | 33 | 34 | 35 | 36 | 37 | 38 | ## 框架原理 39 | 40 | mybatis框架 41 | 42 | ![mybatis框架图](http://7xph6d.com1.z0.glb.clouddn.com/mybatis_%E6%A1%86%E6%9E%B6%E5%9B%BE.png) 43 | 44 | 45 | ## mybatis框架执行过程 46 | 47 | 1、配置mybatis的配置文件,SqlMapConfig.xml(名称不固定) 48 | 49 | 2、通过配置文件,加载mybatis运行环境,创建SqlSessionFactory会话工厂(SqlSessionFactory在实际使用时按单例方式) 50 | 51 | 3、通过SqlSessionFactory创建SqlSession。SqlSession是一个面向用户接口(提供操作数据库方法),实现对象是线程不安全的,建议sqlSession应用场合在方法体内。 52 | 53 | 4、调用sqlSession的方法去操作数据。如果需要提交事务,需要执行SqlSession的commit()方法。 54 | 55 | 5、释放资源,关闭SqlSession 56 | 57 | 58 | ## mybatis开发dao的方法 59 | 60 | 1.原始dao 的方法 61 | 62 | - 需要程序员编写dao接口和实现类 63 | - 需要在dao实现类中注入一个SqlSessionFactory工厂 64 | 65 | 2.mapper代理开发方法(建议使用) 66 | 67 | 只需要程序员编写mapper接口(就是dao接口)。 68 | 程序员在编写mapper.xml(映射文件)和mapper.java需要遵循一个开发规范: 69 | 70 | - mapper.xml中namespace就是mapper.java的类全路径。 71 | - mapper.xml中statement的id和mapper.java中方法名一致。 72 | - mapper.xml中statement的parameterType指定输入参数的类型和mapper.java的方法输入参数类型一致 73 | - mapper.xml中statement的resultType指定输出结果的类型和mapper.java的方法返回值类型一致。 74 | 75 | 76 | SqlMapConfig.xml配置文件:可以配置properties属性、别名、mapper加载。 77 | 78 | 79 | ## 输入映射和输出映射 80 | 81 | - 输入映射: 82 | - parameterType:指定输入参数类型可以简单类型、pojo、hashmap。 83 | - 对于综合查询,建议parameterType使用包装的pojo,有利于系统扩展。 84 | 85 | - 输出映射: 86 | - resultType:查询到的列名和resultType指定的pojo的属性名一致,才能映射成功。 87 | - reusltMap:可以通过resultMap 完成一些高级映射。如果查询到的列名和映射的pojo的属性名不一致时,通过resultMap设置列名和属性名之间的对应关系(映射关系)。可以完成映射。 88 | - 高级映射: 89 | 将关联查询的列映射到一个pojo属性中。(一对一) 90 | 将关联查询的列映射到一个List中。(一对多) 91 | 92 | 93 | ## 动态sql 94 | 95 | - 动态sql:(重点) 96 | - if判断(掌握) 97 | - where 98 | - foreach 99 | - sql片段(掌握) 100 | -------------------------------------------------------------------------------- /mybatis/03 入门程序一.md: -------------------------------------------------------------------------------- 1 | # 03 入门程序一 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [工程结构](#工程结构) 10 | - [Global logging configuration](#global-logging-configuration) 11 | - [Console output...](#console-output) 12 | - [映射文件](#映射文件) 13 | - [程序代码](#程序代码) 14 | - [总结](#总结) 15 | 16 | 17 | 18 | --- 19 | 20 | mybatis入门程序 21 | 22 | ## 工程结构 23 | 24 | 25 | 在IDEA中新建了一个普通的java项目,新建文件夹lib,加入jar包,工程结构如图。 26 | 27 | ![mybatis_入门程序一-工程结构图](http://7xph6d.com1.z0.glb.clouddn.com/mybatis_%E5%85%A5%E9%97%A8%E7%A8%8B%E5%BA%8F%E4%B8%80-%E5%B7%A5%E7%A8%8B%E7%BB%93%E6%9E%84%E5%9B%BE.png) 28 | 29 | 30 | 31 | - log4j.properties 32 | 33 | ``` 34 | # Global logging configuration 35 | log4j.rootLogger=DEBUG, stdout 36 | # Console output... 37 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 38 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 39 | log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n 40 | 41 | ``` 42 | 43 | 44 | 45 | - SqlMapConfig.xml 46 | 47 | ```xml 48 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ``` 70 | 71 | ## 映射文件 72 | 73 | - sqlmap/User.xml 74 | 75 | ```xml 76 | 77 | 80 | 83 | 84 | 85 | 86 | 95 | 98 | 99 | 105 | 108 | 109 | 110 | 111 | ``` 112 | 113 | 114 | 115 | 116 | 在sqlMapConfig.xml中加载User.xml 117 | 118 | ```xml 119 | 120 | 121 | 122 | 123 | ``` 124 | 125 | ## 程序代码 126 | 127 | - po类`User.java` 128 | 129 | ```java 130 | package com.iot.mybatis.po; 131 | 132 | import java.util.Date; 133 | 134 | /** 135 | * Created by Administrator on 2016/2/21. 136 | */ 137 | public class User { 138 | //属性名要和数据库表的字段对应 139 | private int id; 140 | private String username;// 用户姓名 141 | private String sex;// 性别 142 | private Date birthday;// 生日 143 | private String address;// 地址 144 | 145 | public int getId() { 146 | return id; 147 | } 148 | 149 | public void setId(int id) { 150 | this.id = id; 151 | } 152 | 153 | public String getUsername() { 154 | return username; 155 | } 156 | 157 | public void setUsername(String username) { 158 | this.username = username; 159 | } 160 | 161 | public String getSex() { 162 | return sex; 163 | } 164 | 165 | public void setSex(String sex) { 166 | this.sex = sex; 167 | } 168 | 169 | public Date getBirthday() { 170 | return birthday; 171 | } 172 | 173 | public void setBirthday(Date birthday) { 174 | this.birthday = birthday; 175 | } 176 | 177 | public String getAddress() { 178 | return address; 179 | } 180 | 181 | public void setAddress(String address) { 182 | this.address = address; 183 | } 184 | 185 | @Override 186 | public String toString() { 187 | return "User [id=" + id + ", username=" + username + ", sex=" + sex 188 | + ", birthday=" + birthday + ", address=" + address + "]"; 189 | } 190 | } 191 | 192 | ``` 193 | 194 | 195 | - 测试代码 196 | 197 | ```java 198 | package com.iot.mybatis.first; 199 | 200 | import com.iot.mybatis.po.User; 201 | import org.apache.ibatis.io.Resources; 202 | import org.apache.ibatis.session.SqlSession; 203 | import org.apache.ibatis.session.SqlSessionFactory; 204 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 205 | import org.junit.Test; 206 | 207 | import java.io.IOException; 208 | import java.io.InputStream; 209 | import java.util.List; 210 | 211 | /** 212 | * Created by Administrator on 2016/2/23. 213 | */ 214 | public class MybatisFirst { 215 | 216 | //根据id查询用户信息,得到一条记录结果 217 | 218 | @Test 219 | public void findUserByIdTest() throws IOException{ 220 | // mybatis配置文件 221 | String resource = "SqlMapConfig.xml"; 222 | // 得到配置文件流 223 | InputStream inputStream = Resources.getResourceAsStream(resource); 224 | //创建会话工厂,传入mybatis配置文件的信息 225 | SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 226 | 227 | // 通过工厂得到SqlSession 228 | SqlSession sqlSession = sqlSessionFactory.openSession(); 229 | 230 | // 通过SqlSession操作数据库 231 | // 第一个参数:映射文件中statement的id,等于=namespace+"."+statement的id 232 | // 第二个参数:指定和映射文件中所匹配的parameterType类型的参数 233 | // sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象 234 | // selectOne查询出一条记录 235 | User user = sqlSession.selectOne("test.findUserById", 1); 236 | 237 | System.out.println(user); 238 | 239 | // 释放资源 240 | sqlSession.close(); 241 | 242 | } 243 | 244 | // 根据用户名称模糊查询用户列表 245 | @Test 246 | public void findUserByNameTest() throws IOException { 247 | // mybatis配置文件 248 | String resource = "SqlMapConfig.xml"; 249 | // 得到配置文件流 250 | InputStream inputStream = Resources.getResourceAsStream(resource); 251 | 252 | // 创建会话工厂,传入mybatis的配置文件信息 253 | SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() 254 | .build(inputStream); 255 | 256 | // 通过工厂得到SqlSession 257 | SqlSession sqlSession = sqlSessionFactory.openSession(); 258 | // list中的user和映射文件中resultType所指定的类型一致 259 | List list = sqlSession.selectList("test.findUserByName", "小明"); 260 | System.out.println(list); 261 | sqlSession.close(); 262 | 263 | } 264 | 265 | 266 | } 267 | ``` 268 | 269 | 270 | 输出: 271 | 272 | - `findUserByIdTest()` 273 | 274 | ``` 275 | DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 276 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 277 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 278 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 279 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 280 | DEBUG [main] - Opening JDBC Connection 281 | DEBUG [main] - Created connection 1857815974. 282 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6ebc05a6] 283 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 284 | DEBUG [main] - ==> Parameters: 1(Integer) 285 | DEBUG [main] - <== Total: 1 286 | User [id=1, username=王五, sex=2, birthday=null, address=null] 287 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6ebc05a6] 288 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6ebc05a6] 289 | DEBUG [main] - Returned connection 1857815974 to pool. 290 | ``` 291 | 292 | - `findUserByNameTest()` 293 | 294 | ``` 295 | DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 296 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 297 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 298 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 299 | DEBUG [main] - PooledDataSource forcefully closed/removed all connections. 300 | DEBUG [main] - Opening JDBC Connection 301 | DEBUG [main] - Created connection 1596467899. 302 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f282abb] 303 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE username LIKE '%小明%' 304 | DEBUG [main] - ==> Parameters: 305 | DEBUG [main] - <== Total: 3 306 | [User [id=16, username=张小明, sex=1, birthday=null, address=河南郑州], User [id=22, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=25, username=陈小明, sex=1, birthday=null, address=河南郑州]] 307 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f282abb] 308 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f282abb] 309 | DEBUG [main] - Returned connection 1596467899 to pool. 310 | ``` 311 | 312 | 313 | 314 | 315 | ## 总结 316 | 317 | - `parameterType` 318 | 319 | 在映射文件中通过parameterType指定输入参数的类型 320 | 321 | 322 | 323 | 324 | - `resultType` 325 | 326 | 在映射文件中通过resultType指定输出结果的类型 327 | 328 | 329 | 330 | 331 | - `#{}`和`${}` 332 | 333 | `#{}`表示一个占位符号; 334 | 335 | `${}`表示一个拼接符号,会引起sql注入,所以不建议使用 336 | 337 | 338 | 339 | 340 | - `selectOne`和`selectList` 341 | 342 | `selectOne`表示查询一条记录进行映射,使用`selectList`也可以使用,只不过只有一个对象 343 | 344 | `selectList`表示查询出一个列表(参数记录)进行映射,不能够使用`selectOne`查,不然会报下面的错: 345 | 346 | ``` 347 | org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3 348 | ``` 349 | -------------------------------------------------------------------------------- /mybatis/03 入门程序二.md: -------------------------------------------------------------------------------- 1 | # 03 入门程序二 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [映射文件](#映射文件) 10 | - [程序代码](#程序代码) 11 | - [总结](#总结) 12 | - [mybatis和hibernate本质区别和应用场景](#mybatis和hibernate本质区别和应用场景) 13 | - [hibernate](#hibernate) 14 | - [mybatis](#mybatis) 15 | - [什么是OGNL?](#什么是ognl) 16 | 17 | --- 18 | 19 | 20 | 添加、删除、更新用户 21 | 22 | 23 | 24 | ## 映射文件 25 | 26 | - User.xml,在入门程序一基础上增加 27 | 28 | ```xml 29 | 30 | 34 | 35 | 44 | 45 | SELECT LAST_INSERT_ID() 46 | 47 | INSERT INTO user (username,birthday,sex,address)values (#{username},#{birthday},#{sex},#{address}) 48 | 54 | 58 | 59 | 60 | 61 | 64 | 65 | delete from user where id=#{id} 66 | 67 | 68 | 75 | 76 | update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} 77 | where id=#{id} 78 | 79 | 80 | ``` 81 | 82 | 83 | (注:这里的`birthday`字段在mysql表中是DATE类型,在User类中`birthday`属性是java的`java.util.Date`类型,并没有进行转换就插入成功了。 84 | 85 | 看到有的文章说,在字段中有Date和DateTime类型,在插入数据时只要将实体的属性设置成Timestamp就会对应mysql的DateTime类型,Date会对应mysql的Date类型: 86 | `#{modified_date,jdbcType=TIMESTAMP}、#{date,jdbcType=DATE}` 87 | 88 | 我上面的`birthday`,配置成`#{birthday,jdbcType=TIMESTAMP}`,结果也插入成功了,具体实现待查) 89 | 90 | ## 程序代码 91 | 92 | - User.java,在入门程序一基础上增加三个测试方法 93 | 94 | ```java 95 | // 添加用户信息 96 | @Test 97 | public void insertUserTest() throws IOException { 98 | // mybatis配置文件 99 | String resource = "SqlMapConfig.xml"; 100 | // 得到配置文件流 101 | InputStream inputStream = Resources.getResourceAsStream(resource); 102 | 103 | // 创建会话工厂,传入mybatis的配置文件信息 104 | SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() 105 | .build(inputStream); 106 | 107 | // 通过工厂得到SqlSession 108 | SqlSession sqlSession = sqlSessionFactory.openSession(); 109 | // 插入用户对象 110 | User user = new User(); 111 | user.setUsername("王小军"); 112 | user.setBirthday(new Date()); 113 | user.setSex("1"); 114 | user.setAddress("河南郑州"); 115 | 116 | sqlSession.insert("test.insertUser", user); 117 | 118 | // 提交事务 119 | sqlSession.commit(); 120 | 121 | // 获取用户信息主键 122 | System.out.println(user.getId()); 123 | // 关闭会话 124 | sqlSession.close(); 125 | 126 | } 127 | 128 | // 根据id删除 用户信息 129 | @Test 130 | public void deleteUserTest() throws IOException { 131 | // mybatis配置文件 132 | String resource = "SqlMapConfig.xml"; 133 | // 得到配置文件流 134 | InputStream inputStream = Resources.getResourceAsStream(resource); 135 | 136 | // 创建会话工厂,传入mybatis的配置文件信息 137 | SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() 138 | .build(inputStream); 139 | 140 | // 通过工厂得到SqlSession 141 | SqlSession sqlSession = sqlSessionFactory.openSession(); 142 | 143 | // 传入id删除 用户 144 | sqlSession.delete("test.deleteUser", 29); 145 | 146 | // 提交事务 147 | sqlSession.commit(); 148 | 149 | // 关闭会话 150 | sqlSession.close(); 151 | 152 | } 153 | 154 | // 更新用户信息 155 | @Test 156 | public void updateUserTest() throws IOException { 157 | // mybatis配置文件 158 | String resource = "SqlMapConfig.xml"; 159 | // 得到配置文件流 160 | InputStream inputStream = Resources.getResourceAsStream(resource); 161 | 162 | // 创建会话工厂,传入mybatis的配置文件信息 163 | SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() 164 | .build(inputStream); 165 | 166 | // 通过工厂得到SqlSession 167 | SqlSession sqlSession = sqlSessionFactory.openSession(); 168 | // 更新用户信息 169 | 170 | User user = new User(); 171 | //必须设置id 172 | user.setId(27); 173 | user.setUsername("王大军"); 174 | user.setBirthday(new Date()); 175 | user.setSex("2"); 176 | user.setAddress("河南郑州"); 177 | 178 | sqlSession.update("test.updateUser", user); 179 | 180 | // 提交事务 181 | sqlSession.commit(); 182 | 183 | // 关闭会话 184 | sqlSession.close(); 185 | 186 | } 187 | 188 | ``` 189 | 190 | 191 | - 自增主键返回 192 | 193 | ```xml 194 | 195 | SELECT LAST_INSERT_ID() 196 | 197 | ``` 198 | 199 | 如果没有在上面的配置中配置`resultType`,则会报下面的异常 200 | 201 | 202 | ``` 203 | org.apache.ibatis.exceptions.PersistenceException: 204 | ### Error updating database. Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'test.insertUser!selectKey'. It's likely that neither a Result Type nor a Result Map was specified. 205 | ### The error may exist in sqlmap/User.xml 206 | ### The error may involve test.insertUser!selectKey-Inline 207 | ### The error occurred while setting parameters 208 | ### SQL: SELECT LAST_INSERT_ID() 209 | ### Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'test.insertUser!selectKey'. It's likely that neither a Result Type nor a Result Map was specified. 210 | 211 | ... 212 | 213 | Caused by: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'test.insertUser!selectKey'. It's likely that neither a Result Type nor a Result Map was specified. 214 | 215 | 216 | ``` 217 | 218 | 219 | ## 总结 220 | 221 | - `#{}`和`${}` 222 | 223 | `#{}`表示一个占位符号,`#{}`接收输入参数,类型可以是简单类型,pojo、hashmap。 224 | 225 | 如果接收简单类型,`#{}`中可以写成value或其它名称。 226 | 227 | `#{}`接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。 228 | 229 | `${}`表示一个拼接符号,会引用sql注入,所以**不建议使用`${}`**。 230 | 231 | `${}`接收输入参数,类型可以是简单类型,pojo、hashmap。 232 | 233 | 如果接收简单类型,`${}`中只能写成value。 234 | 235 | `${}`接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。 236 | 237 | 238 | 239 | ## mybatis和hibernate本质区别和应用场景 240 | 241 | ### hibernate 242 | 243 | 是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序写sql,sql语句自动生成了。对sql语句进行优化、修改比较困难的。 244 | 245 | **应用场景**:适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa 246 | 247 | 248 | 249 | ### mybatis 250 | 251 | 专注是sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis也可以实现映射(输入映射、输出映射)。 252 | 253 | 应用场景:适用与需求变化较多的项目,比如:互联网项目。 254 | 255 | 企业进行技术选型,以低成本高回报作为技术选型的原则,根据项目组的技术力量进行选择。 256 | 257 | 258 | 259 | ## 什么是OGNL? 260 | 261 | OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个开源项目。Struts框架使用OGNL作为默认的表达式语言。 262 | 263 | 待补充OGNL知识... 264 | 265 | 266 | -------------------------------------------------------------------------------- /mybatis/05 配置文件.md: -------------------------------------------------------------------------------- 1 | # 05 配置文件 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [properties(属性)](#properties属性) 10 | - [settings(全局参数配置)](#settings全局参数配置) 11 | - [typeAliases(类型别名)](#typealiases类型别名) 12 | - [typeHandlers(类型处理器)](#typehandlers类型处理器) 13 | - [mappers(映射配置)](#mappers映射配置) 14 | 15 | 16 | 17 | --- 18 | 19 | 本文主要讲解SqlMapConfig配置文件 20 | 21 | > 参考[mybatis – MyBatis 3 |Configuration](http://www.mybatis.org/mybatis-3/configuration.html) 22 | 23 | SqlMapConfig.xml中配置的内容和顺序如下 24 | 25 | - properties(属性) 26 | - settings(全局配置参数) 27 | - **typeAliases(类型别名)** 28 | - typeHandlers(类型处理器) 29 | - *objectFactory(对象工厂)* 30 | - *plugins(插件)* 31 | - environments(环境集合属性对象) 32 | - environment(环境子属性对象) 33 | - transactionManager(事务管理) 34 | - dataSource(数据源) 35 | - **mappers(映射器)** 36 | 37 | (注:粗体是重点,斜体不常用) 38 | 39 | 40 | 41 | 42 | ## properties(属性) 43 | 44 | 将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。 45 | 46 | 将数据库连接参数只配置在db.properties中。原因:方便对参数进行统一管理,其它xml可以引用该db.properties。 47 | 48 | ```properties 49 | jdbc.driver=com.mysql.jdbc.Driver 50 | jdbc.url=jdbc:mysql://120.25.162.238:3306/mybatis001?characterEncoding=utf-8 51 | jdbc.username=root 52 | jdbc.password=123 53 | ``` 54 | 55 | 在sqlMapConfig.xml加载属性文件: 56 | 57 | ```xml 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | ``` 78 | 79 | 注意: MyBatis 将按照下面的顺序(优先级)来加载属性: 80 | 81 | - 在`properties`元素体内定义的属性首先被读取。 82 | - 然后会读取`properties`元素中resource或url加载的属性,它会覆盖已读取的同名属性。 83 | - 最后读取`parameterType`传递的属性,它会覆盖已读取的同名属性。 84 | 85 | 建议: 86 | 87 | - 不要在`properties`元素体内添加任何属性值,只将属性值定义在properties文件中。 88 | - 在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX 89 | 90 | 91 | 92 | ## settings(全局参数配置) 93 | 94 | mybatis框架在运行时可以调整一些运行参数,比如:开启二级缓存、开启延迟加载... 95 | 96 | 全局参数将会影响mybatis的运行行为。具体参考官网: 97 | 98 | > [mybatis-settings](http://www.mybatis.org/mybatis-3/configuration.html#settings) 99 | 100 | 101 | 102 | 103 | ## typeAliases(类型别名) 104 | 105 | 在mapper.xml中,定义很多的statement,statement需要`parameterType`指定输入参数的类型、需要`resultType`指定输出结果的映射类型。 106 | 107 | 如果在指定类型时输入类型全路径,不方便进行开发,可以针对`parameterType`或`resultType`指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。 108 | 109 | 110 | - mybatis默认支持别名 111 | 112 | > 参考 [typeAliases](http://www.mybatis.org/mybatis-3/configuration.html#typeAliases) 113 | 114 | 115 | | 别名 | 映射的类型 | 116 | |:--- | :---- | 117 | |_byte | byte | 118 | |_long | long | 119 | |_short | short| 120 | |_int| int| 121 | |_integer| int| 122 | |_double| double| 123 | |_float| float | 124 | |_boolean| boolean| 125 | |string | String | 126 | |byte | Byte | 127 | |long | Long| 128 | |short | Short | 129 | |int | Integer | 130 | |integer | Integer | 131 | |double | Double | 132 | |float | Float | 133 | |boolean| Boolean | 134 | |date | Date | 135 | |decimal | BigDecimal | 136 | |bigdecimal| BigDecimal| 137 | 138 | 139 | - 自定义别名 140 | - 单个别名定义 141 | 批量定义别名(常用) 142 | 143 | ```xml 144 | ## SqlMapConfig.xml文件 145 | 146 | 147 | 148 | 152 | 153 | 156 | 157 | 158 | 159 | ``` 160 | 161 | ## typeHandlers(类型处理器) 162 | 163 | mybatis中通过typeHandlers完成jdbc类型和java类型的转换。例如: 164 | 165 | ```xml 166 | 169 | ``` 170 | 171 | mybatis自带的类型处理器基本上满足日常需求,不需要单独定义。 172 | 173 | mybatis支持类型处理器 174 | 175 | > 参考 [typeHandlers](http://www.mybatis.org/mybatis-3/configuration.html#typeHandlers) 176 | 177 | |类型处理器 |Java类型| JDBC类型| 178 | |:---|:---|:----| 179 | |BooleanTypeHandler | Boolean,boolean |任何兼容的布尔值| 180 | |ByteTypeHandler | Byte,byte | 任何兼容的数字或字节类型| 181 | |ShortTypeHandler | Short,short | 任何兼容的数字或短整型| 182 | |IntegerTypeHandler| Integer,int | 任何兼容的数字和整型| 183 | |LongTypeHandler | Long,long |任何兼容的数字或长整型| 184 | |FloatTypeHandler | Float,float | 任何兼容的数字或单精度浮点型| 185 | |DoubleTypeHandler | Double,double | 任何兼容的数字或双精度浮点型| 186 | |BigDecimalTypeHandler | BigDecimal | 任何兼容的数字或十进制小数类型| 187 | |StringTypeHandler | String | CHAR和VARCHAR类型| 188 | |ClobTypeHandler | String | CLOB和LONGVARCHAR类型| 189 | |NStringTypeHandler| String | NVARCHAR和NCHAR类型| 190 | |NClobTypeHandler |String | NCLOB类型 | 191 | |ByteArrayTypeHandler| byte[] | 任何兼容的字节流类型| 192 | |BlobTypeHandler | byte[] | BLOB和LONGVARBINARY类型| 193 | |DateTypeHandler | Date(java.util)| TIMESTAMP类型| 194 | |DateOnlyTypeHandler | Date(java.util)| DATE类型| 195 | |TimeOnlyTypeHandler | Date(java.util)| TIME类型| 196 | |SqlTimestampTypeHandler | Timestamp(java.sql)| TIMESTAMP类型| 197 | |SqlDateTypeHandler | Date(java.sql)| DATE类型| 198 | |SqlTimeTypeHandler | Time(java.sql)| TIME类型| 199 | |ObjectTypeHandler| 任意| 其他或未指定类型| 200 | |EnumTypeHandler | Enumeration类型| VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)| 201 | 202 | 203 | 204 | ## mappers(映射配置) 205 | 206 | - 通过resource加载单个映射文件 207 | 208 | ```xml 209 | 210 | 211 | ``` 212 | 213 | - 通过mapper接口加载单个mapper 214 | 215 | ```xml 216 | 220 | 221 | ``` 222 | 223 | 目录示例 224 | 225 | ``` 226 | com.iot.mybatis.mapper------------------package包 227 | |----UserMapper.java 228 | |----UserMapper.xml 229 | 230 | ``` 231 | 232 | - 批量加载mapper(推荐使用) 233 | 234 | ```xml 235 | 240 | 241 | ``` 242 | 243 | -------------------------------------------------------------------------------- /mybatis/06 输入映射.md: -------------------------------------------------------------------------------- 1 | # 06 输入映射 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [传递pojo的包装对象](#传递pojo的包装对象) 10 | 11 | 12 | 13 | --- 14 | 15 | 本文主要讲解mybatis的输入映射。 16 | 17 | 18 | 通过parameterType指定输入参数的类型,类型可以是 19 | 20 | - 简单类型 21 | - hashmap 22 | - pojo的包装类型 23 | 24 | ## 传递pojo的包装对象 25 | 26 | 需求:完成用户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息、其它信息,比如商品、订单的) 27 | 28 | 29 | 30 | 针对上边需求,建议使用自定义的包装类型的pojo。 31 | 32 | 在包装类型的pojo中将复杂的查询条件包装进去。 33 | 34 | ```java 35 | package com.iot.mybatis.po; 36 | 37 | /** 38 | * Created by Brian on 2016/2/24. 39 | */ 40 | public class UserQueryVo { 41 | 42 | //在这里包装所需要的查询条件 43 | 44 | //用户查询条件 45 | private UserCustom userCustom; 46 | 47 | public UserCustom getUserCustom() { 48 | return userCustom; 49 | } 50 | 51 | public void setUserCustom(UserCustom userCustom) { 52 | this.userCustom = userCustom; 53 | } 54 | 55 | //可以包装其它的查询条件,订单、商品 56 | //.... 57 | 58 | } 59 | ``` 60 | 61 | 其中,UserCustom类继承User 62 | 63 | ```java 64 | public class UserCustom extends User{ 65 | } 66 | ``` 67 | 68 | - mapper.xml 69 | 70 | 在UserMapper.xml中定义用户信息综合查询(查询条件复杂,通过高级查询进行复杂关联查询)。 71 | 72 | ```xml 73 | 77 | 81 | ``` 82 | 83 | 注意不要将`#{userCustom.sex}`中的`userCustom`写成`UserCustom`,前者指属性名(由于使用IDE提示自动补全,所以只是把类型名首字母小写了),后者指类型名,这里是`UserQueryVo`类中的`userCustom`属性,是**属性名**。写错会报如下异常: 84 | 85 | ``` 86 | org.apache.ibatis.exceptions.PersistenceException: 87 | ### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'UserCustom' in 'class com.iot.mybatis.po.UserQueryVo' 88 | ### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'UserCustom' in 'class com.iot.mybatis.po.UserQueryVo' 89 | ``` 90 | 91 | - mapper.java 92 | 93 | ```java 94 | //用户信息综合查询 95 | public List findUserList(UserQueryVo userQueryVo) throws Exception; 96 | ``` 97 | 98 | - 测试代码 99 | 100 | ```java 101 | //用户信息的综合 查询 102 | @Test 103 | public void testFindUserList() throws Exception { 104 | SqlSession sqlSession = sqlSessionFactory.openSession(); 105 | //创建UserMapper对象,mybatis自动生成mapper代理对象 106 | UserMapper userMapper sqlSession.getMapper(UserMapper.class); 107 | //创建包装对象,设置查询条件 108 | UserQueryVo userQueryVo = new UserQueryVo(); 109 | UserCustom userCustom = new UserCustom(); 110 | //由于这里使用动态sql,如果不设置某个值,条件不会拼接在sql中 111 | userCustom.setSex("1"); 112 | userCustom.setUsername("张三"); 113 | userQueryVo.setUserCustom(userCustom); 114 | //调用userMapper的方法 115 | List list = userMapper.findUserList(userQueryVo); 116 | System.out.println(list); 117 | } 118 | ``` 119 | -------------------------------------------------------------------------------- /mybatis/07 输出映射.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(7)-输出映射 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [resultType](#resulttype) 10 | - [输出简单类型](#输出简单类型) 11 | - [输出pojo对象和pojo列表](#输出pojo对象和pojo列表) 12 | - [resultMap](#resultmap) 13 | - [resultMap使用方法](#resultmap使用方法) 14 | - [小结](#小结) 15 | 16 | 17 | 18 | --- 19 | 20 | 本文主要讲解mybatis的输出映射。 21 | 22 | 23 | 输出映射有两种方式 24 | 25 | - `resultType` 26 | - `resultMap` 27 | 28 | 29 | ## resultType 30 | 31 | - 使用 `resultType` 进行输出映射,只有查询出来的列名和 pojo 中的属性名一致,该列才可以映射成功。 32 | - 如果查询出来的列名和 pojo 中的属性名全部不一致,没有创建 pojo 对象。 33 | - 只要查询出来的列名和 pojo 中的属性有一个一致,就会创建 pojo 对象。 34 | 35 | 36 | 37 | ### 输出简单类型 38 | 39 | 需求:用户信息的综合查询列表总数,通过查询总数和上边用户综合查询列表才可以实现分页。 40 | 41 | 42 | 43 | 44 | - mapper.xml 45 | 46 | ```xml 47 | 51 | 54 | ``` 55 | 56 | - mapper.java 57 | 58 | ```java 59 | //用户信息综合查询总数 60 | @Test 61 | public void testFindUserCount() throws Exception { 62 | 63 | SqlSession sqlSession = sqlSessionFactory.openSession(); 64 | 65 | //创建UserMapper对象,mybatis自动生成mapper代理对象 66 | UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 67 | 68 | //创建包装对象,设置查询条件 69 | UserQueryVo userQueryVo = new UserQueryVo(); 70 | UserCustom userCustom = new UserCustom(); 71 | //由于这里使用动态sql,如果不设置某个值,条件不会拼接在sql中 72 | userCustom.setSex("1"); 73 | userCustom.setUsername("小"); 74 | userQueryVo.setUserCustom(userCustom); 75 | //调用userMapper的方法 76 | int count = userMapper.findUserCount(userQueryVo); 77 | System.out.println(count); 78 | } 79 | ``` 80 | 81 | 82 | - 小结 83 | 84 | 查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。 85 | 86 | 87 | 88 | 89 | ### 输出pojo对象和pojo列表 90 | 91 | 不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中 `resultType` 指定的类型是一样的。 92 | 93 | 在mapper.java指定的方法返回值类型不一样: 94 | 95 | - 输出单个pojo对象,方法返回值是单个对象类型 96 | 97 | ```java 98 | //根据id查询用户信息 99 | public User findUserById(int id) throws Exception; 100 | ``` 101 | 102 | - 输出pojo对象list,方法返回值是List 103 | 104 | ```java 105 | //根据用户名列查询用户列表 106 | public List findUserByName(String name) throws Exception; 107 | ``` 108 | 109 | 110 | 生成的动态代理对象中是根据mapper方法的返回值类型确定是调用`selectOne`(返回单个对象调用)还是`selectList` (返回集合对象调用 ). 111 | 112 | 113 | 114 | ## resultMap 115 | 116 | mybatis中使用resultMap完成高级输出结果映射。(一对多,多对多) 117 | 118 | 119 | ### resultMap使用方法 120 | 121 | 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。 122 | 123 | 1.定义resultMap 124 | 125 | 2.使用resultMap作为statement的输出映射类型 126 | 127 | - 定义reusltMap 128 | 129 | ```xml 130 | 136 | 137 | 142 | 143 | 149 | 150 | 151 | ``` 152 | 153 | - 使用resultMap作为statement的输出映射类型 154 | 155 | ```xml 156 | 159 | 162 | 163 | ``` 164 | 165 | - mapper.java 166 | 167 | ```java 168 | //根据id查询用户信息,使用resultMap输出 169 | public User findUserByIdResultMap(int id) throws Exception; 170 | ``` 171 | 172 | - 测试代码 173 | 174 | ```java 175 | @Test 176 | public void testFindUserByIdResultMap() throws Exception { 177 | SqlSession sqlSession = sqlSessionFactory.openSession(); 178 | //创建UserMapper对象,mybatis自动生成mapper代理对象 179 | UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 180 | //调用userMapper的方法 181 | User user = userMapper.findUserByIdResultMap(1); 182 | System.out.println(user); 183 | } 184 | ``` 185 | 186 | 187 | ### 小结 188 | 189 | 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。 190 | 191 | 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。 192 | -------------------------------------------------------------------------------- /mybatis/08 动态sql.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(8)-动态sql 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [if判断](#if判断) 10 | - [sql片段(重点)](#sql片段重点) 11 | - [foreach标签](#foreach标签) 12 | 13 | 14 | 15 | --- 16 | 17 | mybatis核心:对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。 18 | 19 | 20 | 21 | **需求** 22 | 23 | - 用户信息综合查询列表和用户信息查询列表总数这两个statement的定义使用动态sql。 24 | - 对查询条件进行判断,如果输入参数不为空才进行查询条件拼接。 25 | 26 | 27 | 28 | ## if判断 29 | 30 | - mapper.xml 31 | 32 | ```xml 33 | 37 | 54 | 55 | 59 | 72 | 73 | ``` 74 | 75 | - 测试结果 76 | 77 | 1.注释掉 `testFindUserList()` 方法中的 `userCustom.setUsername("张三");` 78 | 79 | 80 | ```java 81 | //由于这里使用动态sql,如果不设置某个值,条件不会拼接在sql中 82 | userCustom.setSex("1"); 83 | //userCustom.setUsername("张三"); 84 | userQueryVo.setUserCustom(userCustom); 85 | ``` 86 | 87 | 输出 88 | 89 | ``` 90 | DEBUG [main] - Checking to see if class com.iot.mybatis.mapper.UserMapper matches criteria [is assignable to Object] 91 | DEBUG [main] - Checking to see if class com.iot.mybatis.mapper.UserMapperTest matches criteria [is assignable to Object] 92 | DEBUG [main] - Opening JDBC Connection 93 | DEBUG [main] - Created connection 352359770. 94 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1500955a] 95 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE user.sex=? 96 | DEBUG [main] - ==> Parameters: 1(String) 97 | DEBUG [main] - <== Total: 6 98 | [User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市], User [id=16, username=张小明, sex=1, birthday=null, address=河南郑州], User [id=22, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州], User [id=25, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=28, username=王小军, sex=1, birthday=Tue Feb 23 00:00:00 CST 2016, address=河南郑州]] 99 | ``` 100 | 101 | 可以看到sql语句为`reparing: SELECT * FROM user WHERE user.sex=? `,没有username的部分 102 | 103 | 104 | 2.`userQueryVo` 设为null,则 `userCustom` 为null 105 | 106 | ```java 107 | //List list = userMapper.findUserList(userQueryVo); 108 | List list = userMapper.findUserList(null); 109 | ``` 110 | 111 | 输出 112 | 113 | ``` 114 | DEBUG [main] - ==> Preparing: SELECT * FROM user 115 | DEBUG [main] - ==> Parameters: 116 | DEBUG [main] - <== Total: 9 117 | [User [id=1, username=王五, sex=2, birthday=null, address=null], User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市], User [id=16, username=张小明, sex=1, birthday=null, address=河南郑州], User [id=22, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州], User [id=25, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=26, username=王五, sex=null, birthday=null, address=null], User [id=27, username=王大军, sex=2, birthday=Tue Feb 23 00:00:00 CST 2016, address=河南郑州], User [id=28, username=王小军, sex=1, birthday=Tue Feb 23 00:00:00 CST 2016, address=河南郑州]] 118 | 119 | ``` 120 | 121 | 可以看到sql语句变为了`SELECT * FROM user` 122 | 123 | 124 | 125 | ## sql片段(重点) 126 | 127 | 将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。 128 | 129 | 130 | - 定义sql片段 131 | 132 | ```xml 133 | 139 | 140 | 141 | 142 | AND user.sex = #{userCustom.sex} 143 | 144 | 145 | AND user.username LIKE '%${userCustom.username}%' 146 | 147 | 148 | 149 | ``` 150 | 151 | - 引用sql片段 152 | 153 | ```xml 154 | 158 | 168 | ``` 169 | 170 | 171 | 172 | ## foreach标签 173 | 174 | 向sql传递数组或List,mybatis使用foreach解析 175 | 176 | 在用户查询列表和查询总数的statement中增加多个id输入查询。两种方法,sql语句如下: 177 | 178 | - `SELECT * FROM USER WHERE id=1 OR id=10 OR id=16` 179 | - `SELECT * FROM USER WHERE id IN(1,10,16)` 180 | 181 | 一个使用OR,一个使用IN 182 | 183 | 184 | - 在输入参数类型中添加`List ids`传入多个id 185 | 186 | ```java 187 | public class UserQueryVo { 188 | 189 | //传入多个id 190 | private List ids; 191 | 192 | getter、setter方法 193 | 。。。 194 | } 195 | ``` 196 | 197 | - 修改mapper.xml 198 | 199 | ```xml 200 | 201 | 208 | 211 | 212 | 213 | id=#{user_id} 214 | 215 | 216 | 217 | 221 | 222 | 223 | ``` 224 | 225 | 226 | - 测试代码 227 | 228 | 在`testFindUserList`中加入 229 | 230 | ```java 231 | //传入多个id 232 | List ids = new ArrayList(); 233 | ids.add(1); 234 | ids.add(10); 235 | ids.add(16); 236 | //将ids通过userQueryVo传入statement中 237 | userQueryVo.setIds(ids); 238 | ``` 239 | 240 | -------------------------------------------------------------------------------- /mybatis/09 订单商品数据模型分析.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(9)-订单商品数据模型分析 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [数据模型分析思路](#数据模型分析思路) 10 | - [数据模型分析](#数据模型分析) 11 | - [订单商品数据模型建表sql](#订单商品数据模型建表sql) 12 | 13 | 14 | 15 | --- 16 | 17 | 本文对接下来几篇博客中用到的数据模型进行分析,并附上建表sql文件和测试数据文件 18 | 19 | ## 数据模型分析思路 20 | 21 | - 每张表记录的数据内容 22 | 23 | 分模块对每张表记录的内容进行熟悉,相当于你学习系统需求(功能)的过程。 24 | 25 | - 每张表重要的字段设置 26 | 27 | 非空字段、外键字段 28 | 29 | - 数据库级别表与表之间的关系 30 | 31 | 外键关系 32 | 33 | - 表与表之间的业务关系 34 | 35 | 在分析表与表之间的业务关系时一定要建立在某个业务意义基础上去分析。 36 | 37 | 38 | ## 数据模型分析 39 | 40 | 41 | ![数据模型分析](http://7xph6d.com1.z0.glb.clouddn.com/mybatis_%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B%E5%88%86%E6%9E%90.png) 42 | 43 | - 用户表user:记录了购买商品的用户信息 44 | - 订单表orders:记录了用户所创建的订单(购买商品的订单) 45 | - 订单明细表orderdetail:记录了订单的详细信息即购买商品的信息 46 | - 商品表items:记录了商品信息 47 | 48 | 49 | 表与表之间的业务关系: 50 | 51 | 在分析表与表之间的业务关系时需要建立在某个业务意义基础上去分析。先分析数据级别之间有关系的表之间的业务关系: 52 | 53 | - usre和orders: 54 | 55 | user--->orders:一个用户可以创建多个订单,一对多 56 | orders--->user:一个订单只由一个用户创建,一对一 57 | 58 | - orders和orderdetail: 59 | 60 | orders--->orderdetail:一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系 61 | 62 | orderdetail---> orders:一个订单明细只能包括在一个订单中,一对一 63 | 64 | 65 | - orderdetail和itesm: 66 | 67 | orderdetail--->itesms:一个订单明细只对应一个商品信息,一对一 68 | 69 | items---> orderdetail:一个商品可以包括在多个订单明细 ,一对多 70 | 71 | 再分析数据库级别没有关系的表之间是否有业务关系: 72 | 73 | - orders和items: 74 | 75 | orders和items之间可以通过orderdetail表建立关系。 76 | 77 | 78 | 79 | ## 订单商品数据模型建表sql 80 | 81 | ```sql 82 | /* 83 | SQLyog v10.2 84 | MySQL - 5.1.72-community : Database - mybatis 85 | ********************************************************************* 86 | */ 87 | 88 | 89 | /*!40101 SET NAMES utf8 */; 90 | 91 | /*!40101 SET SQL_MODE=''*/; 92 | 93 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 94 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 95 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 96 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 97 | /*Table structure for table `items` */ 98 | 99 | CREATE TABLE `items` ( 100 | `id` int(11) NOT NULL AUTO_INCREMENT, 101 | `name` varchar(32) NOT NULL COMMENT '商品名称', 102 | `price` float(10,1) NOT NULL COMMENT '商品定价', 103 | `detail` text COMMENT '商品描述', 104 | `pic` varchar(64) DEFAULT NULL COMMENT '商品图片', 105 | `createtime` datetime NOT NULL COMMENT '生产日期', 106 | PRIMARY KEY (`id`) 107 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 108 | 109 | /*Table structure for table `orderdetail` */ 110 | 111 | CREATE TABLE `orderdetail` ( 112 | `id` int(11) NOT NULL AUTO_INCREMENT, 113 | `orders_id` int(11) NOT NULL COMMENT '订单id', 114 | `items_id` int(11) NOT NULL COMMENT '商品id', 115 | `items_num` int(11) DEFAULT NULL COMMENT '商品购买数量', 116 | PRIMARY KEY (`id`), 117 | KEY `FK_orderdetail_1` (`orders_id`), 118 | KEY `FK_orderdetail_2` (`items_id`), 119 | CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 120 | CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 121 | ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; 122 | 123 | /*Table structure for table `orders` */ 124 | 125 | CREATE TABLE `orders` ( 126 | `id` int(11) NOT NULL AUTO_INCREMENT, 127 | `user_id` int(11) NOT NULL COMMENT '下单用户id', 128 | `number` varchar(32) NOT NULL COMMENT '订单号', 129 | `createtime` datetime NOT NULL COMMENT '创建订单时间', 130 | `note` varchar(100) DEFAULT NULL COMMENT '备注', 131 | PRIMARY KEY (`id`), 132 | KEY `FK_orders_1` (`user_id`), 133 | CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 134 | ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; 135 | 136 | /*Table structure for table `user` */ 137 | 138 | CREATE TABLE `user` ( 139 | `id` int(11) NOT NULL AUTO_INCREMENT, 140 | `username` varchar(32) NOT NULL COMMENT '用户名称', 141 | `birthday` date DEFAULT NULL COMMENT '生日', 142 | `sex` char(1) DEFAULT NULL COMMENT '性别', 143 | `address` varchar(256) DEFAULT NULL COMMENT '地址', 144 | PRIMARY KEY (`id`) 145 | ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8; 146 | 147 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 148 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 149 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 150 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 151 | 152 | ``` 153 | 154 | 测试数据 155 | 156 | ```sql 157 | /* 158 | SQLyog v10.2 159 | MySQL - 5.1.72-community : Database - mybatis 160 | ********************************************************************* 161 | */ 162 | 163 | 164 | /*!40101 SET NAMES utf8 */; 165 | 166 | /*!40101 SET SQL_MODE=''*/; 167 | 168 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 169 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 170 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 171 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 172 | /*Data for the table `items` */ 173 | 174 | insert into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'台式机',3000.0,'该电脑质量非常好!!!!',NULL,'2015-02-03 13:22:53'),(2,'笔记本',6000.0,'笔记本性能好,质量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'背包',200.0,'名牌背包,容量大质量好!!!!',NULL,'2015-02-06 13:23:02'); 175 | 176 | /*Data for the table `orderdetail` */ 177 | 178 | insert into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3); 179 | 180 | /*Data for the table `orders` */ 181 | 182 | insert into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL); 183 | 184 | /*Data for the table `user` */ 185 | 186 | insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'王五',NULL,'2',NULL),(10,'张三','2014-07-10','1','北京市'),(16,'张小明',NULL,'1','河南郑州'),(22,'陈小明',NULL,'1','河南郑州'),(24,'张三丰',NULL,'1','河南郑州'),(25,'陈小明',NULL,'1','河南郑州'),(26,'王五',NULL,NULL,NULL); 187 | 188 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 189 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 190 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 191 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 192 | ``` 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /mybatis/10 一对一查询.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(10)-一对一查询 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [resultType实现](#resulttype实现) 10 | - [resultMap实现](#resultmap实现) 11 | - [resultType和resultMap实现一对一查询小结](#resulttype和resultmap实现一对一查询小结) 12 | 13 | 14 | 15 | --- 16 | 17 | 18 | 本文使用两种方式(resultType和resultMap)实现一对一查询,查询订单信息,关联查询创建订单的用户信息 19 | 20 | 21 | ## resultType实现 22 | 23 | - sql语句 24 | 25 | 26 | 确定查询的主表:订单表 27 | 28 | 确定查询的关联表:用户表 29 | 30 | 关联查询使用内连接?还是外连接? 31 | 32 | 由于orders表中有一个外键(user_id),通过外键关联查询用户表只能查询出一条记录,可以使用内连接。 33 | 34 | ```sql 35 | SELECT 36 | orders.*, 37 | USER.username, 38 | USER.sex, 39 | USER.address 40 | FROM 41 | orders, 42 | USER 43 | WHERE orders.user_id = user.id 44 | ``` 45 | 46 | - 创建pojo 47 | 48 | 将上边sql查询的结果映射到pojo中,pojo中必须包括所有查询列名。 49 | 50 | 原始的Orders.java不能映射全部字段,需要新创建的pojo。 51 | 52 | 创建一个pojo继承包括查询字段较多的po类。 53 | 54 | 对应数据表的几个pojo类(Items,Orderdetail,Orders)就是把该类的属性名设为和数据表列字段名相同,并为这些属性添加getter和setter,在这里就不贴代码了,只贴出对应于关联查询的自定义pojo类`OrdersCustom`的代码 55 | 56 | ```java 57 | package com.iot.mybatis.po; 58 | 59 | /** 60 | * 61 | *

Title: OrdersCustom

62 | *

Description: 订单的扩展类

63 | */ 64 | //通过此类映射订单和用户查询的结果,让此类继承包括 字段较多的pojo类 65 | public class OrdersCustom extends Orders{ 66 | 67 | //添加用户属性 68 | /*USER.username, 69 | USER.sex, 70 | USER.address */ 71 | 72 | private String username; 73 | private String sex; 74 | private String address; 75 | 76 | 77 | public String getUsername() { 78 | return username; 79 | } 80 | public void setUsername(String username) { 81 | this.username = username; 82 | } 83 | 84 | public String getSex() { 85 | return sex; 86 | } 87 | public void setSex(String sex) { 88 | this.sex = sex; 89 | } 90 | 91 | public String getAddress() { 92 | return address; 93 | } 94 | public void setAddress(String address) { 95 | this.address = address; 96 | } 97 | 98 | } 99 | 100 | ``` 101 | 102 | 103 | 104 | - mapper.xml 105 | 106 | ```xml 107 | 108 | 119 | ``` 120 | 121 | 122 | - mapper.java 123 | 124 | ```java 125 | //查询订单关联查询用户信息 126 | public List findOrdersUser()throws Exception; 127 | } 128 | ``` 129 | 130 | 131 | 132 | ## resultMap实现 133 | 134 | 使用resultMap将查询结果中的订单信息映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。 135 | 136 | - 定义resultMap 137 | 138 | 139 | ```xml 140 | 143 | 144 | 145 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 159 | 160 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | ``` 172 | 173 | 174 | - statement定义 175 | 176 | 177 | ```xml 178 | 179 | 190 | ``` 191 | 192 | - mapper.java 193 | 194 | ```java 195 | //查询订单关联查询用户使用resultMap 196 | public List findOrdersUserResultMap()throws Exception; 197 | ``` 198 | 199 | - 测试代码 200 | 201 | ```java 202 | @Test 203 | public void testFindOrdersUserResultMap() throws Exception { 204 | 205 | SqlSession sqlSession = sqlSessionFactory.openSession(); 206 | // 创建代理对象 207 | OrdersMapperCustom ordersMapperCustom = sqlSession 208 | .getMapper(OrdersMapperCustom.class); 209 | 210 | // 调用maper的方法 211 | List list = ordersMapperCustom.findOrdersUserResultMap(); 212 | 213 | System.out.println(list); 214 | 215 | sqlSession.close(); 216 | } 217 | ``` 218 | 219 | ## resultType和resultMap实现一对一查询小结 220 | 221 | 实现一对一查询: 222 | 223 | - resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果没有查询结果的特殊要求建议使用resultType。 224 | - resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的属性中。 225 | - resultMap可以实现延迟加载,resultType无法实现延迟加载。 226 | 227 | 228 | -------------------------------------------------------------------------------- /mybatis/11 一对多查询.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(11)-一对多查询 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [示例](#示例) 10 | - [小结](#小结) 11 | 12 | 13 | 14 | --- 15 | 16 | 17 | 本文实现一对多查询,查询订单及订单明细的信息 18 | 19 | ## 示例 20 | 21 | - sql 22 | 23 | 确定主查询表:订单表 24 | 确定关联查询表:订单明细表 25 | 在一对一查询基础上添加订单明细表关联即可。 26 | 27 | ```sql 28 | SELECT 29 | orders.*, 30 | user.username, 31 | user.sex, 32 | user.address, 33 | orderdetail.id orderdetail_id, 34 | orderdetail.items_id, 35 | orderdetail.items_num, 36 | orderdetail.orders_id 37 | FROM 38 | orders, 39 | user, 40 | orderdetail 41 | WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id 42 | ``` 43 | 44 | **注意上面的`orderdetail.id (AS) orderdetail_id`,这里需要取别名,否则由于orders表也有id字段,在后面映射时会冲突** 45 | 46 | - 映射思路 47 | 48 | 使用resultType将上边的查询结果映射到pojo中,订单信息的就是重复。 49 | 50 | 对orders映射不能出现重复记录。 51 | 52 | 在orders.java类中添加`List orderDetails`属性。 53 | 最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderDetails属性中。 54 | 55 | 映射成的orders记录数为两条(orders信息不重复),每个orders中的orderDetails属性存储了该订单所对应的订单明细。 56 | 57 | 58 | - 在orders中添加list订单明细属性 59 | 60 | ```java 61 | //订单明细 62 | private List orderdetails; 63 | ``` 64 | 65 | - mapper.xml 66 | 67 | ```xml 68 | 69 | 85 | ``` 86 | 87 | - resultMap定义 88 | 89 | ```xml 90 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 105 | 106 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | ``` 117 | 118 | 119 | - mapper.java 120 | 121 | ```java 122 | //查询订单(关联用户)及订单明细 123 | public List findOrdersAndOrderDetailResultMap()throws Exception; 124 | ``` 125 | 126 | 127 | ## 小结 128 | 129 | mybatis使用resultMap的collection对关联查询的多条记录映射到一个list集合属性中。 130 | 131 | 使用resultType实现:将订单明细映射到orders中的orderdetails中,需要自己处理,使用双重循环遍历,去掉重复记录,将订单明细放在orderdetails中。 132 | 133 | 134 | 另外,下面这篇文章对一对多的resultMap机制解释的很清楚: 135 | 136 | > [MyBatis:一对多表关系详解(从案例中解析)](http://blog.csdn.net/xzm_rainbow/article/details/15336933) 137 | 138 | -------------------------------------------------------------------------------- /mybatis/12 多对多查询.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(12)-多对多查询 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [示例](#示例) 10 | - [多对多查询总结](#多对多查询总结) 11 | - [resultMap总结](#resultmap总结) 12 | 13 | 14 | 15 | --- 16 | 17 | 18 | 本文实现多对多查询,查询用户及用户购买商品信息。 19 | 20 | ## 示例 21 | 22 | 查询主表是:用户表 23 | 24 | 关联表:由于用户和商品没有直接关联,通过订单和订单明细进行关联,所以关联表:orders、orderdetail、items 25 | 26 | 27 | - sql 28 | 29 | ```sql 30 | SELECT 31 | orders.*, 32 | user.username, 33 | user.sex, 34 | user.address, 35 | orderdetail.id orderdetail_id, 36 | orderdetail.items_id, 37 | orderdetail.items_num, 38 | orderdetail.orders_id, 39 | items.name items_name, 40 | items.detail items_detail, 41 | items.price items_price 42 | FROM 43 | orders, 44 | user, 45 | orderdetail, 46 | items 47 | WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id 48 | ``` 49 | 50 | 51 | - 映射思路 52 | 53 | 将用户信息映射到user中。 54 | 55 | 在user类中添加订单列表属性`List orderslist`,将用户创建的订单映射到orderslist 56 | 57 | 在Orders中添加订单明细列表属性`Listorderdetials`,将订单的明细映射到orderdetials 58 | 59 | 在OrderDetail中添加`Items`属性,将订单明细所对应的商品映射到Items 60 | 61 | 62 | - mapper.xml 63 | 64 | ```xml 65 | 66 | 86 | ``` 87 | 88 | - resultMap 89 | 90 | ```xml 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | ``` 133 | 134 | - mapper.java 135 | 136 | ```java 137 | //查询用户购买商品信息 138 | public List findUserAndItemsResultMap()throws Exception; 139 | ``` 140 | 141 | ## 多对多查询总结 142 | 143 | 将查询用户购买的商品信息明细清单,(用户名、用户地址、购买商品名称、购买商品时间、购买商品数量) 144 | 145 | 针对上边的需求就使用resultType将查询到的记录映射到一个扩展的pojo中,很简单实现明细清单的功能。 146 | 147 | 一对多是多对多的特例,如下需求: 148 | 149 | 查询用户购买的商品信息,用户和商品的关系是多对多关系。 150 | 151 | - 需求1: 152 | 153 | 查询字段:用户账号、用户名称、用户性别、商品名称、商品价格(最常见) 154 | 155 | 企业开发中常见明细列表,用户购买商品明细列表, 156 | 157 | 使用resultType将上边查询列映射到pojo输出。 158 | 159 | - 需求2: 160 | 161 | 查询字段:用户账号、用户名称、购买商品数量、商品明细(鼠标移上显示明细) 162 | 163 | 使用resultMap将用户购买的商品明细列表映射到user对象中。 164 | 165 | 总结: 166 | 167 | 使用resultMap是针对那些对查询结果映射有特殊要求的功能,比如特殊要求映射成list中包括多个list。 168 | 169 | 170 | 171 | ## resultMap总结 172 | 173 | - resultType 174 | - 作用:将查询结果按照sql列名pojo属性名一致性映射到pojo中。 175 | - 场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)即可。 176 | 177 | - resultMap 178 | 179 | 使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。 180 | 181 | association: 182 | 183 | - 作用:将关联查询信息映射到一个pojo对象中。 184 | - 场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。 185 | 186 | 使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。 187 | 188 | collection: 189 | 190 | - 作用:将关联查询信息映射到一个list集合中。 191 | - 场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。如果使用resultType无法将查询结果映射到list集合中。 192 | -------------------------------------------------------------------------------- /mybatis/13 延迟加载.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(13)-延迟加载 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [使用association实现延迟加载](#使用association实现延迟加载) 10 | - [延迟加载思考](#延迟加载思考) 11 | - [IDEA的debug小现象](#idea的debug小现象) 12 | 13 | 14 | 15 | --- 16 | 17 | 18 | resultMap可以实现高级映射(使用`association`、`collection`实现一对一及一对多映射),`association`、`collection`具备延迟加载功能。 19 | 20 | 延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。 21 | 22 | 23 | 需求: 24 | 25 | 如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。 26 | 27 | ## 使用association实现延迟加载 28 | 29 | - mapper.xml 30 | 31 | 需要定义两个mapper的方法对应的statement。 32 | 33 | 1.只查询订单信息 34 | 35 | `SELECT * FROM orders` 36 | 37 | 在查询订单的statement中使用association去延迟加载(执行)下边的satatement(关联查询用户信息) 38 | 39 | ```xml 40 | 41 | 44 | ``` 45 | 46 | 2.关联查询用户信息 47 | 48 | 通过上边查询到的订单信息中user_id去关联查询用户信息,使用UserMapper.xml中的findUserById 49 | 50 | ```xml 51 | 54 | ``` 55 | 56 | 上边先去执行findOrdersUserLazyLoading,当需要去查询用户的时候再去执行findUserById,通过resultMap的定义将延迟加载执行配置起来。 57 | 58 | 59 | - 延迟加载resultMap 60 | 61 | ```xml 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 80 | 83 | 84 | 85 | 86 | 87 | 88 | ``` 89 | 90 | **与非延迟加载的主要区别就在`association`标签属性多了`select`和`column`** 91 | 92 | ```xml 93 | 96 | ``` 97 | 98 | - mapper.java 99 | 100 | ```java 101 | //查询订单关联查询用户,用户信息是延迟加载 102 | public List findOrdersUserLazyLoading()throws Exception; 103 | ``` 104 | 105 | 106 | 107 | - 测试思路 108 | - 执行上边mapper方法(`findOrdersUserLazyLoading`),内部去调用`com.iot.mybatis.mapper.OrdersMapperCustom`中的`findOrdersUserLazyLoading`只查询orders信息(单表)。 109 | - 在程序中去遍历上一步骤查询出的List,当我们调用Orders中的getUser方法时,开始进行延迟加载。 110 | - 延迟加载,去调用UserMapper.xml中findUserbyId这个方法获取用户信息。 111 | 112 | - 延迟加载配置 113 | 114 | mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中setting配置。 115 | 116 | 在mybatis核心配置文件中配置:lazyLoadingEnabled、aggressiveLazyLoading 117 | 118 | 119 | | 设置项 | 描述 | 允许值| 默认值 | 120 | | :--- |:---- |:--- |:---- | 121 | |lazyLoadingEnabled|全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载|true/false|false| 122 | |aggressiveLazyLoading| 当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。|true/false|true| 123 | 124 | 125 | 在SqlMapConfig.xml中配置: 126 | 127 | ```xml 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | ``` 137 | 138 | - 测试代码 139 | 140 | ```java 141 | // 查询订单关联查询用户,用户信息使用延迟加载 142 | @Test 143 | public void testFindOrdersUserLazyLoading() throws Exception { 144 | SqlSession sqlSession = sqlSessionFactory.openSession();// 创建代理对象 145 | OrdersMapperCustom ordersMapperCustom = sqlSession 146 | .getMapper(OrdersMapperCustom.class); 147 | // 查询订单信息(单表) 148 | List list = ordersMapperCustom.findOrdersUserLazyLoading(); 149 | 150 | // 遍历上边的订单列表 151 | for (Orders orders : list) { 152 | // 执行getUser()去查询用户信息,这里实现按需加载 153 | User user = orders.getUser(); 154 | System.out.println(user); 155 | } 156 | } 157 | ``` 158 | 159 | 160 | ## 延迟加载思考 161 | 162 | 不使用mybatis提供的association及collection中的延迟加载功能,如何实现延迟加载?? 163 | 164 | 实现方法如下: 165 | 166 | 定义两个mapper方法: 167 | 168 | - 查询订单列表 169 | - 根据用户id查询用户信息 170 | 171 | 实现思路: 172 | 173 | 先去查询第一个mapper方法,获取订单信息列表;在程序中(service),按需去调用第二个mapper方法去查询用户信息。 174 | 175 | 总之,使用延迟加载方法,先去查询简单的sql(最好单表,也可以关联查询),再去按需要加载关联查询的其它信息。 176 | 177 | 178 | ## IDEA的debug小现象 179 | 180 | 使用的是intellij IDEA 15.0.2 181 | 182 | 先说一下结果吧,IDEA在debug和run条件下,打印结果不同 183 | 184 | 我为了验证延迟加载前的user是否为空,在Orders类中加入了 185 | 186 | ```java 187 | public void print(){ 188 | System.out.println("----test-print-----"+user+" user==null: "+(user==null)); 189 | } 190 | ``` 191 | 192 | 测试代码如下: 193 | 194 | ```java 195 | // 查询订单关联查询用户,用户信息使用延迟加载 196 | @Test 197 | public void testFindOrdersUserLazyLoading() throws Exception { 198 | SqlSession sqlSession = sqlSessionFactory.openSession();// 创建代理对象 199 | OrdersMapperCustom ordersMapperCustom = sqlSession 200 | .getMapper(OrdersMapperCustom.class); 201 | // 查询订单信息(单表) 202 | List list = ordersMapperCustom.findOrdersUserLazyLoading(); 203 | 204 | // 遍历上边的订单列表 205 | for (Orders orders : list) { 206 | // 执行getUser()去查询用户信息,这里实现按需加载 207 | //User user = orders.getUser(); 208 | //System.out.println(user); 209 | orders.print(); 210 | } 211 | 212 | } 213 | ``` 214 | 215 | 然后分别run和debug 216 | 217 | - run输出 218 | 219 | ``` 220 | DEBUG [main] - Opening JDBC Connection 221 | DEBUG [main] - Created connection 110771485. 222 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 223 | DEBUG [main] - ==> Preparing: SELECT * FROM orders 224 | DEBUG [main] - ==> Parameters: 225 | DEBUG [main] - <== Total: 3 226 | ----test-print-----null user==null: true 227 | ----test-print-----null user==null: true 228 | ----test-print-----null user==null: true 229 | ``` 230 | 231 | - debug输出 232 | 233 | 在`List list = ordersMapperCustom.findOrdersUserLazyLoading();`打断点,运行完这句,日志输出为: 234 | 235 | ``` 236 | DEBUG [main] - Opening JDBC Connection 237 | DEBUG [main] - Created connection 1219273867. 238 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@48aca48b] 239 | DEBUG [main] - ==> Preparing: SELECT * FROM orders 240 | DEBUG [main] - ==> Parameters: 241 | DEBUG [main] - <== Total: 3 242 | ``` 243 | 244 | 但是当你点开`list`属性时,控制台又输出了,而且可以看到list里的user是有内容的 245 | 246 | ``` 247 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 248 | DEBUG [main] - ==> Parameters: 1(Integer) 249 | DEBUG [main] - <== Total: 1 250 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 251 | DEBUG [main] - ==> Parameters: 10(Integer) 252 | DEBUG [main] - <== Total: 1 253 | ``` 254 | 255 | 运行完所有程序,控制台输出为: 256 | 257 | ``` 258 | DEBUG [main] - Opening JDBC Connection 259 | DEBUG [main] - Created connection 1219273867. 260 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@48aca48b] 261 | DEBUG [main] - ==> Preparing: SELECT * FROM orders 262 | DEBUG [main] - ==> Parameters: 263 | DEBUG [main] - <== Total: 3 264 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 265 | DEBUG [main] - ==> Parameters: 1(Integer) 266 | DEBUG [main] - <== Total: 1 267 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 268 | DEBUG [main] - ==> Parameters: 10(Integer) 269 | DEBUG [main] - <== Total: 1 270 | ----test-print-----User [id=1, username=王五, sex=2, birthday=null, address=null] user==null: false 271 | ----test-print-----User [id=1, username=王五, sex=2, birthday=null, address=null] user==null: false 272 | ----test-print-----User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市] user==null: false 273 | ``` 274 | 275 | 276 | 所以,我觉得应该是在debug时,查看属性的话,IDEA会自动调用get相应的方法,从而触发user的查询。延迟加载的源码实现以后我会阅读,把这个问题弄清楚。 277 | 278 | -------------------------------------------------------------------------------- /mybatis/14 查询缓存之一级缓存.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(14)-查询缓存之一级缓存 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [查询缓存](#查询缓存) 10 | - [一级缓存](#一级缓存) 11 | - [一级缓存工作原理](#一级缓存工作原理) 12 | - [一级缓存测试](#一级缓存测试) 13 | - [一级缓存应用](#一级缓存应用) 14 | 15 | 16 | 17 | --- 18 | 19 | 本文主要讲mybatis的一级缓存,一级缓存是SqlSession级别的缓存。 20 | 21 | 22 | ## 查询缓存 23 | 24 | mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。 25 | 26 | mybaits提供一级缓存,和二级缓存。 27 | 28 | ![查询缓存](http://7xph6d.com1.z0.glb.clouddn.com/mybatis_%E6%9F%A5%E8%AF%A2%E7%BC%93%E5%AD%98.png) 29 | 30 | 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 31 | 32 | 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。 33 | 34 | 为什么要用缓存? 35 | 36 | 如果缓存中有数据就不用从数据库中获取,大大提高系统性能。 37 | 38 | 39 | ## 一级缓存 40 | 41 | ### 一级缓存工作原理 42 | 43 | ![一级缓存工作原理](http://7xph6d.com1.z0.glb.clouddn.com/mybatis_%E4%B8%80%E7%BA%A7%E7%BC%93%E5%AD%98%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.png) 44 | 45 | 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。 46 | 47 | 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。 48 | 49 | 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。 50 | 51 | 52 | ### 一级缓存测试 53 | 54 | mybatis默认支持一级缓存,不需要在配置文件去配置。 55 | 56 | 按照上边一级缓存原理步骤去测试。 57 | 58 | 测试代码 59 | 60 | ```java 61 | // 一级缓存测试 62 | @Test 63 | public void testCache1() throws Exception { 64 | SqlSession sqlSession = sqlSessionFactory.openSession();// 创建代理对象 65 | UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 66 | 67 | // 下边查询使用一个SqlSession 68 | // 第一次发起请求,查询id为1的用户 69 | User user1 = userMapper.findUserById(1); 70 | System.out.println(user1); 71 | 72 | // 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。 73 | 74 | // 更新user1的信息 75 | // user1.setUsername("测试用户22"); 76 | // userMapper.updateUser(user1); 77 | // //执行commit操作去清空缓存 78 | // sqlSession.commit(); 79 | 80 | // 第二次发起请求,查询id为1的用户 81 | User user2 = userMapper.findUserById(1); 82 | System.out.println(user2); 83 | 84 | sqlSession.close(); 85 | 86 | } 87 | ``` 88 | 89 | 90 | 91 | 1.不执行更新操作,输出: 92 | 93 | ``` 94 | DEBUG [main] - Opening JDBC Connection 95 | DEBUG [main] - Created connection 110771485. 96 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 97 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 98 | DEBUG [main] - ==> Parameters: 1(Integer) 99 | DEBUG [main] - <== Total: 1 100 | User [id=1, username=王五, sex=2, birthday=null, address=null] 101 | User [id=1, username=王五, sex=2, birthday=null, address=null] 102 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 103 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 104 | DEBUG [main] - Returned connection 110771485 to pool. 105 | ``` 106 | 107 | 2.取消测试代码中更新的的注释,输出: 108 | 109 | ``` 110 | DEBUG [main] - Opening JDBC Connection 111 | DEBUG [main] - Created connection 110771485. 112 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 113 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 114 | DEBUG [main] - ==> Parameters: 1(Integer) 115 | DEBUG [main] - <== Total: 1 116 | User [id=1, username=王五, sex=2, birthday=null, address=null] 117 | DEBUG [main] - ==> Preparing: update user set username=?,birthday=?,sex=?,address=? where id=? 118 | DEBUG [main] - ==> Parameters: 测试用户22(String), null, 2(String), null, 1(Integer) 119 | DEBUG [main] - <== Updates: 1 120 | DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 121 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 122 | DEBUG [main] - ==> Parameters: 1(Integer) 123 | DEBUG [main] - <== Total: 1 124 | User [id=1, username=测试用户22, sex=2, birthday=null, address=null] 125 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 126 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d] 127 | DEBUG [main] - Returned connection 110771485 to pool. 128 | ``` 129 | 130 | 131 | 132 | ### 一级缓存应用 133 | 134 | 正式开发,是将mybatis和spring进行整合开发,事务控制在service中。 135 | 136 | 一个service方法中包括 很多mapper方法调用。 137 | 138 | ``` 139 | service{ 140 | //开始执行时,开启事务,创建SqlSession对象 141 | //第一次调用mapper的方法findUserById(1) 142 | 143 | //第二次调用mapper的方法findUserById(1),从一级缓存中取数据 144 | //方法结束,sqlSession关闭 145 | } 146 | ``` 147 | 148 | 如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。 149 | -------------------------------------------------------------------------------- /mybatis/15 查询缓存之二级缓存.md: -------------------------------------------------------------------------------- 1 | # mybatis学习笔记(15)-查询缓存之二级缓存 2 | 3 | 标签: mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [二级缓存原理](#二级缓存原理) 10 | - [开启二级缓存](#开启二级缓存) 11 | - [调用pojo类实现序列化接口](#调用pojo类实现序列化接口) 12 | - [测试方法](#测试方法) 13 | - [useCache配置](#usecache配置) 14 | - [刷新缓存(就是清空缓存)](#刷新缓存(就是清空缓存)) 15 | - [应用场景和局限性](#应用场景和局限性) 16 | 17 | 18 | 19 | --- 20 | 21 | 本文主要讲mybatis的二级缓存,二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。 22 | 23 | 24 | ## 二级缓存原理 25 | 26 | 27 | ![二级缓存原理](http://7xph6d.com1.z0.glb.clouddn.com/mybatis_%E4%BA%8C%E7%BA%A7%E7%BC%93%E5%AD%98%E5%8E%9F%E7%90%86%E5%9B%BE.png) 28 | 29 | 首先开启mybatis的二级缓存. 30 | 31 | sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。 32 | 33 | 如果SqlSession3去执行相同mapper下sql,执行commit提交,清空该mapper下的二级缓存区域的数据。 34 | 35 | sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。 36 | 37 | 二级缓存与一级缓存区别,**二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域**。 38 | 39 | UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。 40 | 41 | 42 | ## 开启二级缓存 43 | 44 | mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。 45 | 46 | 在核心配置文件SqlMapConfig.xml中加入`` 47 | 48 | |设置项|描述|允许值|默认值| 49 | |:---|:---|:---|:---| 50 | |cacheEnabled|对在此配置文件下的所有cache 进行全局性开/关设置。| true/false | true| 51 | 52 | ```xml 53 | 54 | 55 | ``` 56 | 57 | 在UserMapper.xml中开启二缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。 58 | 59 | ```xml 60 | 61 | 62 | 63 | 64 | ... 65 | 66 | 67 | ``` 68 | 69 | 70 | ## 调用pojo类实现序列化接口 71 | 72 | ```java 73 | public class User implements Serializable{ 74 | .... 75 | } 76 | ``` 77 | 78 | 为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定在内存。 79 | 80 | ## 测试方法 81 | 82 | ```java 83 | // 二级缓存测试 84 | @Test 85 | public void testCache2() throws Exception { 86 | SqlSession sqlSession1 = sqlSessionFactory.openSession(); 87 | SqlSession sqlSession2 = sqlSessionFactory.openSession(); 88 | SqlSession sqlSession3 = sqlSessionFactory.openSession(); 89 | // 创建代理对象 90 | UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); 91 | // 第一次发起请求,查询id为1的用户 92 | User user1 = userMapper1.findUserById(1); 93 | System.out.println(user1); 94 | 95 | //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域 96 | sqlSession1.close(); 97 | 98 | 99 | // //使用sqlSession3执行commit()操作 100 | // UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class); 101 | // User user = userMapper3.findUserById(1); 102 | // user.setUsername("张明明"); 103 | // userMapper3.updateUser(user); 104 | // //执行提交,清空UserMapper下边的二级缓存 105 | // sqlSession3.commit(); 106 | // sqlSession3.close(); 107 | 108 | 109 | 110 | UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); 111 | // 第二次发起请求,查询id为1的用户 112 | User user2 = userMapper2.findUserById(1); 113 | System.out.println(user2); 114 | 115 | sqlSession2.close(); 116 | } 117 | ``` 118 | 119 | 120 | 121 | 1.无更新,输出 122 | 123 | ``` 124 | DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0 125 | DEBUG [main] - Opening JDBC Connection 126 | DEBUG [main] - Created connection 103887628. 127 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 128 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 129 | DEBUG [main] - ==> Parameters: 1(Integer) 130 | DEBUG [main] - <== Total: 1 131 | User [id=1, username=测试用户22, sex=2, birthday=null, address=null] 132 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 133 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 134 | DEBUG [main] - Returned connection 103887628 to pool. 135 | DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5 136 | User [id=1, username=测试用户22, sex=2, birthday=null, address=null] 137 | ``` 138 | 139 | 2.有更新,输出 140 | 141 | 142 | ``` 143 | DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0 144 | DEBUG [main] - Opening JDBC Connection 145 | DEBUG [main] - Created connection 103887628. 146 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 147 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 148 | DEBUG [main] - ==> Parameters: 1(Integer) 149 | DEBUG [main] - <== Total: 1 150 | User [id=1, username=测试用户22, sex=2, birthday=null, address=null] 151 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 152 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 153 | DEBUG [main] - Returned connection 103887628 to pool. 154 | DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5 155 | DEBUG [main] - Opening JDBC Connection 156 | DEBUG [main] - Checked out connection 103887628 from pool. 157 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 158 | DEBUG [main] - ==> Preparing: update user set username=?,birthday=?,sex=?,address=? where id=? 159 | DEBUG [main] - ==> Parameters: 张明明(String), null, 2(String), null, 1(Integer) 160 | DEBUG [main] - <== Updates: 1 161 | DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 162 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 163 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 164 | DEBUG [main] - Returned connection 103887628 to pool. 165 | DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.3333333333333333 166 | DEBUG [main] - Opening JDBC Connection 167 | DEBUG [main] - Checked out connection 103887628 from pool. 168 | DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 169 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=? 170 | DEBUG [main] - ==> Parameters: 1(Integer) 171 | DEBUG [main] - <== Total: 1 172 | User [id=1, username=张明明, sex=2, birthday=null, address=null] 173 | DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 174 | DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c] 175 | DEBUG [main] - Returned connection 103887628 to pool. 176 | ``` 177 | 178 | 179 | ## useCache配置 180 | 181 | 在statement中设置`useCache=false`可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。 182 | 183 | ` 172 | SELECT items.* FROM items 173 | 174 | 175 | 176 | 177 | 178 |
179 | ``` 180 | 181 | - ItemsMapperCustom.java 182 | 183 | ```java 184 | public interface ItemsMapperCustom { 185 | //商品查询列表 186 | List findItemsList(ItemsQueryVo itemsQueryVo)throws Exception; 187 | } 188 | ``` 189 | 190 | - po类`ItemsCustom` 191 | 192 | ```java 193 | package com.iot.learnssm.firstssm.po; 194 | 195 | /** 196 | * Created by Brian on 2016/3/2. 197 | * 商品信息的扩展类 198 | */ 199 | public class ItemsCustom extends Items{ 200 | //添加商品信息的扩展属性 201 | } 202 | ``` 203 | 204 | - 输入pojo的包装类 205 | 206 | ```java 207 | package com.iot.learnssm.firstssm.po; 208 | 209 | /** 210 | * Created by Brian on 2016/3/2. 211 | */ 212 | public class ItemsQueryVo { 213 | 214 | //商品信息 215 | private Items items; 216 | 217 | //为了系统 可扩展性,对原始生成的po进行扩展 218 | private ItemsCustom itemsCustom; 219 | 220 | public Items getItems() { 221 | return items; 222 | } 223 | 224 | public void setItems(Items items) { 225 | this.items = items; 226 | } 227 | 228 | public ItemsCustom getItemsCustom() { 229 | return itemsCustom; 230 | } 231 | 232 | public void setItemsCustom(ItemsCustom itemsCustom) { 233 | this.itemsCustom = itemsCustom; 234 | } 235 | } 236 | ``` 237 | 238 | 239 | 整合好dao后的工程目录如图 240 | 241 | -------------------------------------------------------------------------------- /springmvc/08 springmvc整合mybatis之service.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(8)-springmvc整合mybatis之service 2 | 3 | 标签: springmvc mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [定义service接口](#定义service接口) 10 | - [在spring容器配置service](#在spring容器配置service) 11 | - [事务控制](#事务控制) 12 | 13 | 14 | 15 | --- 16 | 17 | 18 | 本文记录如何整合service,包括定义spring接口,在spring容器配置service以及事务控制。让spring管理service接口。 19 | 20 | 21 | ## 定义service接口 22 | 23 | ```java 24 | public interface ItemsService { 25 | //商品查询列表 26 | List findItemsList(ItemsQueryVo itemsQueryVo) throws Exception; 27 | 28 | } 29 | ``` 30 | 31 | ```java 32 | public class ItemsServiceImpl implements ItemsService { 33 | 34 | @Autowired 35 | private ItemsMapperCustom itemsMapperCustom; 36 | 37 | public List findItemsList(ItemsQueryVo itemsQueryVo) throws Exception { 38 | return itemsMapperCustom.findItemsList(itemsQueryVo); 39 | } 40 | } 41 | ``` 42 | 43 | ## 在spring容器配置service 44 | 45 | 46 | 在`resources/spring`下创建applicationContext-service.xml,文件中配置service。 47 | 48 | ```xml 49 | 52 | 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | ## 事务控制 60 | 61 | 在`resources/spring`下创建applicationContext-transaction.xml,在applicationContext-transaction.xml中使用spring声明式事务控制方法。 62 | 63 | ```xml 64 | 65 | 72 | 73 | 74 | 77 | 78 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | ``` 103 | -------------------------------------------------------------------------------- /springmvc/09 springmvc整合mybatis之controller.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(9)-springmvc整合mybatis之controller 2 | 3 | 标签: springmvc mybatis 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [springmvc.xml](#springmvcxml) 10 | - [配置web.xml](#配置webxml) 11 | - [编写Controller(就是Handler)](#编写controller就是handler) 12 | - [编写jsp](#编写jsp) 13 | 14 | 15 | 16 | --- 17 | 18 | 19 | 本文介绍如何配置springmvc配置文件和web.xml,以及如何编写controller,jsp 20 | 21 | 22 | ## springmvc.xml 23 | 24 | 在`resources/spring`文件下下创建springmvc.xml文件,配置处理器映射器、适配器、视图解析器。 25 | 26 | ```xml 27 | 34 | 35 | 38 | 41 | 42 | 43 | 44 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ``` 63 | 64 | ## 配置web.xml 65 | 66 | 67 | 参考入门程序,web.xml 68 | 69 | ```xml 70 | 71 | 74 | firstssm 75 | 76 | 77 | 78 | contextConfigLocation 79 | WEB-INF/classes/spring/applicationContext-*.xml 80 | 81 | 82 | 83 | org.springframework.web.context.ContextLoaderListener 84 | 85 | 86 | 87 | 88 | 89 | springmvc 90 | org.springframework.web.servlet.DispatcherServlet 91 | 94 | 95 | contextConfigLocation 96 | classpath:spring/springmvc.xml 97 | 98 | 99 | 100 | 101 | springmvc 102 | 109 | *.action 110 | 111 | 112 | 113 | 114 | index.html 115 | index.htm 116 | index.jsp 117 | default.html 118 | default.htm 119 | default.jsp 120 | 121 | 122 | ``` 123 | 124 | 这个文件有两个作用: 125 | 126 | - 配置前端控制器(`DispatcherServlet`) 127 | - 加载spring容器:添加spring容器监听器,加载spring容器,使用通配符加载`spring/`下的配置文件 128 | - applicationContext-dao.xml 129 | - applicationContext-service.xml 130 | - applicationContext-transaction.xml 131 | 132 | 133 | 134 | ## 编写Controller(就是Handler) 135 | 136 | 137 | ```java 138 | package com.iot.learnssm.firstssm.controller; 139 | 140 | 141 | import com.iot.learnssm.firstssm.po.Items; 142 | import com.iot.learnssm.firstssm.po.ItemsCustom; 143 | import com.iot.learnssm.firstssm.service.ItemsService; 144 | import org.springframework.beans.factory.annotation.Autowired; 145 | import org.springframework.stereotype.Controller; 146 | import org.springframework.web.bind.annotation.RequestMapping; 147 | import org.springframework.web.servlet.ModelAndView; 148 | 149 | import java.util.ArrayList; 150 | import java.util.List; 151 | 152 | /** 153 | * Created by brian on 2016/3/2. 154 | */ 155 | 156 | //使用@Controller来标识它是一个控制器 157 | @Controller 158 | //为了对url进行分类管理 ,可以在这里定义根路径,最终访问url是根路径+子路径 159 | //比如:商品列表:/items/queryItems.action 160 | public class ItemsController { 161 | 162 | @Autowired 163 | private ItemsService itemsService; 164 | 165 | //商品查询列表 166 | @RequestMapping("/queryItems") 167 | //实现 对queryItems方法和url进行映射,一个方法对应一个url 168 | //一般建议将url和方法写成一样 169 | public ModelAndView queryItems() throws Exception{ 170 | //调用service查找数据库,查询商品列表 171 | List itemsList = itemsService.findItemsList(null); 172 | 173 | //返回ModelAndView 174 | ModelAndView modelAndView = new ModelAndView(); 175 | //相当于request的setAttribute方法,在jsp页面中通过itemsList取数据 176 | modelAndView.addObject("itemsList",itemsList); 177 | 178 | //指定视图 179 | //下边的路径,如果在视图解析器中配置jsp的路径前缀和后缀,修改为items/itemsList 180 | //modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp"); 181 | //下边的路径配置就可以不在程序中指定jsp路径的前缀和后缀 182 | modelAndView.setViewName("items/itemsList"); 183 | 184 | return modelAndView; 185 | } 186 | 187 | 188 | } 189 | ``` 190 | 191 | ## 编写jsp 192 | 193 | 服务器路径为`WEB-INF/jsp/items/itemsList.jsp` 194 | 195 | ```jsp 196 | <%@ page language="java" contentType="text/html; charset=UTF-8" 197 | pageEncoding="UTF-8"%> 198 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 199 | <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> 200 | 201 | 202 | 203 | 204 | 查询商品列表 205 | 206 | 207 |
208 | 查询条件: 209 | 210 | 211 | 212 | 213 |
214 | 商品列表: 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 |
商品名称商品价格生产日期商品描述操作
${item.name }${item.price }${item.detail }修改
236 |
237 | 238 | 239 | 240 | ``` 241 | 242 | 243 | -------------------------------------------------------------------------------- /springmvc/10 springmvc注解开发之商品修改功能.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(10)-springmvc注解开发之商品修改功能 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [需求](#需求) 10 | - [开发mapper](#开发mapper) 11 | - [开发service](#开发service) 12 | - [开发controller](#开发controller) 13 | - [`@RequestMapping`](#@requestmapping) 14 | - [controller方法的返回值](#controller方法的返回值) 15 | 16 | 17 | 18 | --- 19 | 20 | 本文以商品修改为例,记录springmvc的注解开发,包括mapper,service,controller,@RequestMapping,controller方法的返回值等 21 | 22 | ## 需求 23 | 24 | 操作流程: 25 | 26 | - 1.进入商品查询列表页面 27 | - 2.点击修改,进入商品修改页面,页面中显示了要修改的商品。要修改的商品从数据库查询,根据商品id(主键)查询商品信息 28 | - 3.在商品修改页面,修改商品信息,修改后,点击提交 29 | 30 | ## 开发mapper 31 | 32 | mapper: 33 | 34 | - 根据id查询商品信息 35 | - 根据id更新Items表的数据 36 | 37 | 不用开发了,使用逆向工程生成的代码。 38 | 39 | ## 开发service 40 | 41 | 在`com.iot.learnssm.firstssm.service.ItemsService`中添加两个接口 42 | 43 | ```java 44 | //根据id查询商品信息 45 | /** 46 | * 47 | *

Title: findItemsById

48 | *

Description:

49 | * @param id 查询商品的id 50 | * @return 51 | * @throws Exception 52 | */ 53 | ItemsCustom findItemsById(Integer id) throws Exception; 54 | 55 | //修改商品信息 56 | /** 57 | * 58 | *

Title: updateItems

59 | *

Description:

60 | * @param id 修改商品的id 61 | * @param itemsCustom 修改的商品信息 62 | * @throws Exception 63 | */ 64 | void updateItems(Integer id,ItemsCustom itemsCustom) throws Exception; 65 | 66 | ``` 67 | 68 | 在`com.iot.learnssm.firstssm.service.impl.ItemsServiceImpl`中实现接口,增加`itemsMapper`属性 69 | 70 | 71 | ```java 72 | @Autowired 73 | private ItemsMapper itemsMapper; 74 | 75 | public ItemsCustom findItemsById(Integer id) throws Exception { 76 | Items items = itemsMapper.selectByPrimaryKey(id); 77 | //中间对商品信息进行业务处理 78 | //.... 79 | //返回ItemsCustom 80 | ItemsCustom itemsCustom = new ItemsCustom(); 81 | //将items的属性值拷贝到itemsCustom 82 | BeanUtils.copyProperties(items, itemsCustom); 83 | 84 | return itemsCustom; 85 | } 86 | 87 | public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception { 88 | //添加业务校验,通常在service接口对关键参数进行校验 89 | //校验 id是否为空,如果为空抛出异常 90 | 91 | //更新商品信息使用updateByPrimaryKeyWithBLOBs根据id更新items表中所有字段,包括 大文本类型字段 92 | //updateByPrimaryKeyWithBLOBs要求必须转入id 93 | itemsCustom.setId(id); 94 | itemsMapper.updateByPrimaryKeyWithBLOBs(itemsCustom); 95 | } 96 | ``` 97 | 98 | ## 开发controller 99 | 100 | 方法: 101 | 102 | - 商品信息修改页面显示 103 | - 商品信息修改提交 104 | 105 | ```java 106 | //使用@Controller来标识它是一个控制器 107 | @Controller 108 | //为了对url进行分类管理 ,可以在这里定义根路径,最终访问url是根路径+子路径 109 | //比如:商品列表:/items/queryItems.action 110 | //@RequestMapping("/items") 111 | public class ItemsController { 112 | 113 | @Autowired 114 | private ItemsService itemsService; 115 | 116 | //商品查询列表 117 | @RequestMapping("/queryItems") 118 | //实现 对queryItems方法和url进行映射,一个方法对应一个url 119 | //一般建议将url和方法写成一样 120 | public ModelAndView queryItems() throws Exception{ 121 | //调用service查找数据库,查询商品列表 122 | List itemsList = itemsService.findItemsList(null); 123 | 124 | //返回ModelAndView 125 | ModelAndView modelAndView = new ModelAndView(); 126 | //相当于request的setAttribute方法,在jsp页面中通过itemsList取数据 127 | modelAndView.addObject("itemsList",itemsList); 128 | 129 | //指定视图 130 | //下边的路径,如果在视图解析器中配置jsp的路径前缀和后缀,修改为items/itemsList 131 | //modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp"); 132 | //下边的路径配置就可以不在程序中指定jsp路径的前缀和后缀 133 | modelAndView.setViewName("items/itemsList"); 134 | 135 | return modelAndView; 136 | } 137 | 138 | 139 | //商品信息修改页面显示 140 | @RequestMapping("/editItems") 141 | //限制http请求方法,可以post和get 142 | //@RequestMapping(value="/editItems",method={RequestMethod.POST, RequestMethod.GET}) 143 | public ModelAndView editItems()throws Exception { 144 | 145 | //调用service根据商品id查询商品信息 146 | ItemsCustom itemsCustom = itemsService.findItemsById(1); 147 | 148 | // 返回ModelAndView 149 | ModelAndView modelAndView = new ModelAndView(); 150 | 151 | //将商品信息放到model 152 | modelAndView.addObject("itemsCustom", itemsCustom); 153 | 154 | //商品修改页面 155 | modelAndView.setViewName("items/editItems"); 156 | 157 | return modelAndView; 158 | } 159 | 160 | //商品信息修改提交 161 | @RequestMapping("/editItemsSubmit") 162 | public ModelAndView editItemsSubmit(HttpServletRequest request, Integer id, ItemsCustom itemsCustom)throws Exception { 163 | 164 | //调用service更新商品信息,页面需要将商品信息传到此方法 165 | itemsService.updateItems(id, itemsCustom); 166 | 167 | //返回ModelAndView 168 | ModelAndView modelAndView = new ModelAndView(); 169 | //返回一个成功页面 170 | modelAndView.setViewName("success"); 171 | return modelAndView; 172 | } 173 | 174 | } 175 | 176 | ``` 177 | 178 | 179 | 180 | ## `@RequestMapping` 181 | 182 | - url映射 183 | 184 | 定义controller方法对应的url,进行处理器映射使用。 185 | 186 | 187 | - 窄化请求映射 188 | 189 | ```java 190 | //使用@Controller来标识它是一个控制器 191 | @Controller 192 | //为了对url进行分类管理 ,可以在这里定义根路径,最终访问url是根路径+子路径 193 | //比如:商品列表:/items/queryItems.action 194 | @RequestMapping("/items") 195 | public class ItemsController { 196 | ``` 197 | 198 | 199 | - 限制http请求方法 200 | 201 | 出于安全性考虑,对http的链接进行方法限制。 202 | 203 | ```java 204 | //商品信息修改页面显示 205 | //@RequestMapping("/editItems") 206 | //限制http请求方法,可以post和get 207 | @RequestMapping(value="/editItems",method={RequestMethod.POST, RequestMethod.GET}) 208 | public ModelAndView editItems()throws Exception { 209 | ``` 210 | 211 | 如果限制请求为post方法,进行get请求,即将上面代码的注解改为`@RequestMapping(value="/editItems",method={RequestMethod.POST})` 212 | 213 | 报错,状态码405: 214 | 215 | ![1547948554726](assets/1547948554726.png) 216 | 217 | 218 | 219 | 220 | ## controller方法的返回值 221 | 222 | - 返回`ModelAndView` 223 | 224 | 需要方法结束时,定义ModelAndView,将model和view分别进行设置。 225 | 226 | 227 | - 返回string 228 | 229 | 如果controller方法返回string 230 | 231 | 1.表示返回逻辑视图名。 232 | 233 | 真正视图(jsp路径)=前缀+逻辑视图名+后缀 234 | 235 | ```java 236 | @RequestMapping(value="/editItems",method={RequestMethod.POST,RequestMethod.GET}) 237 | //@RequestParam里边指定request传入参数名称和形参进行绑定。 238 | //通过required属性指定参数是否必须要传入 239 | //通过defaultValue可以设置默认值,如果id参数没有传入,将默认值和形参绑定。 240 | //public String editItems(Model model, @RequestParam(value="id",required=true) Integer items_id)throws Exception { 241 | public String editItems(Model model)throws Exception { 242 | 243 | //调用service根据商品id查询商品信息 244 | ItemsCustom itemsCustom = itemsService.findItemsById(1); 245 | 246 | //通过形参中的model将model数据传到页面 247 | //相当于modelAndView.addObject方法 248 | model.addAttribute("itemsCustom", itemsCustom); 249 | 250 | return "items/editItems"; 251 | } 252 | ``` 253 | 254 | 2.redirect重定向 255 | 256 | 商品修改提交后,重定向到商品查询列表。 257 | 258 | redirect重定向特点:浏览器地址栏中的url会变化。修改提交的request数据无法传到重定向的地址。因为重定向后重新进行request(request无法共享) 259 | 260 | ```java 261 | //重定向到商品查询列表 262 | //return "redirect:queryItems.action"; 263 | ``` 264 | 265 | 3.forward页面转发 266 | 267 | 通过forward进行页面转发,浏览器地址栏url不变,request可以共享。 268 | 269 | ```java 270 | //页面转发 271 | return "forward:queryItems.action"; 272 | ``` 273 | 274 | 275 | 276 | - 返回void 277 | 278 | 在controller方法形参上可以定义request和response,使用request或response指定响应结果: 279 | 280 | 1.使用request转向页面,如下: 281 | 282 | `request.getRequestDispatcher("页面路径").forward(request, response);` 283 | 284 | 2.也可以通过response页面重定向: 285 | 286 | `response.sendRedirect("url")` 287 | 288 | 3.也可以通过response指定响应结果,例如响应json数据如下: 289 | 290 | ```java 291 | response.setCharacterEncoding("utf-8"); 292 | response.setContentType("application/json;charset=utf-8"); 293 | response.getWriter().write("json串"); 294 | ``` 295 | -------------------------------------------------------------------------------- /springmvc/11 springmvc注解开发之简单参数绑定.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(11)-springmvc注解开发之简单参数绑定 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [spring参数绑定过程](#spring参数绑定过程) 10 | - [默认支持的类型](#默认支持的类型) 11 | - [简单类型](#简单类型) 12 | - [pojo绑定](#pojo绑定) 13 | - [自定义参数绑定实现日期类型绑定](#自定义参数绑定实现日期类型绑定) 14 | - [springmvc和struts2的区别](#springmvc和struts2的区别) 15 | 16 | 17 | 18 | --- 19 | 20 | 21 | 本文主要介绍注解开发的简单参数绑定,包括简单类型、简单pojo以及自定义绑定实现类型转换 22 | 23 | ## spring参数绑定过程 24 | 25 | 从客户端请求key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上。 26 | 27 | springmvc中,接收页面提交的数据是通过方法形参来接收。而不是在controller类定义成员变更接收!!!! 28 | 29 | ![1547948611496](assets/1547948611496.png) 30 | 31 | 32 | 33 | 34 | ## 默认支持的类型 35 | 36 | 直接在controller方法形参上定义下边类型的对象,就可以使用这些对象。在参数绑定过程中,如果遇到下边类型直接进行绑定。 37 | 38 | - `HttpServletRequest`:通过request对象获取请求信息 39 | - `HttpServletResponse`:通过response处理响应信息 40 | - `HttpSession`:通过session对象得到session中存放的对象 41 | - `Model/ModelMap`:model是一个接口,modelMap是一个接口实现。作用:将model数据填充到request域。 42 | 43 | 44 | ## 简单类型 45 | 46 | 通过`@RequestParam`对简单类型的参数进行绑定。如果不使用`@RequestParam`,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。 47 | 48 | 如果使用`@RequestParam`,不用限制request传入参数名称和controller方法的形参名称一致。 49 | 50 | 通过required属性指定参数是否必须要传入,如果设置为true,没有传入参数,报下边错误: 51 | 52 | ![1547948632377](assets/1547948632377.png) 53 | 54 | 55 | 56 | 57 | ```java 58 | @RequestMapping(value="/editItems",method={RequestMethod.POST,RequestMethod.GET}) 59 | //@RequestParam里边指定request传入参数名称和形参进行绑定。 60 | //通过required属性指定参数是否必须要传入 61 | //通过defaultValue可以设置默认值,如果id参数没有传入,将默认值和形参绑定。 62 | public String editItems(Model model,@RequestParam(value="id",required=true) Integer items_id)throws Exception { 63 | 64 | ``` 65 | 66 | 67 | ## pojo绑定 68 | 69 | 页面中input的name和controller的pojo形参中的属性名称一致,将页面中数据绑定到pojo。 70 | 71 | 注意:这里只是要求name和形参的**属性名**一致,而不是要求和形参的**名称**一致,这点不要混淆了,框架会进入形参内部自动匹配pojo类的属性名。(我没看源码,但应该是用反射实现的) 72 | 73 | 74 | 75 | 页面定义: 76 | 77 | ```jsp 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | ``` 88 | 89 | controller的pojo形参的定义: 90 | 91 | ```java 92 | public class Items { 93 | private Integer id; 94 | 95 | private String name; 96 | 97 | private Float price; 98 | 99 | private String pic; 100 | 101 | private Date createtime; 102 | 103 | private String detail; 104 | ``` 105 | 106 | 107 | ## 自定义参数绑定实现日期类型绑定 108 | 109 | 对于controller形参中pojo对象,如果属性中有日期类型,需要自定义参数绑定。 110 | 111 | 将请求日期数据串传成日期类型,要转换的日期类型和pojo中日期属性的类型保持一致。本文示例中,自定义参数绑定将日期串转成java.util.Date类型。 112 | 113 | 需要向处理器适配器中注入自定义的参数绑定组件。 114 | 115 | 116 | - 自定义日期类型绑定 117 | 118 | ```java 119 | public class CustomDateConverter implements Converter{ 120 | public Date convert(String s) { 121 | //实现 将日期串转成日期类型(格式是yyyy-MM-dd HH:mm:ss) 122 | 123 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 124 | 125 | try { 126 | //转成直接返回 127 | return simpleDateFormat.parse(s); 128 | } catch (ParseException e) { 129 | // TODO Auto-generated catch block 130 | e.printStackTrace(); 131 | } 132 | //如果参数绑定失败返回null 133 | return null; 134 | 135 | } 136 | } 137 | ``` 138 | 139 | 140 | - 配置方式 141 | 142 | ```xml 143 | 144 | ``` 145 | 146 | ```xml 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | ``` 158 | 159 | 160 | ## springmvc和struts2的区别 161 | 162 | - 1.springmvc基于方法开发的,struts2基于类开发的。 163 | 164 | springmvc将url和controller方法映射。映射成功后springmvc生成一个Handler对象,对象中只包括了一个method。方法执行结束,形参数据销毁。springmvc的controller开发类似service开发。 165 | 166 | - 2.springmvc可以进行单例开发,并且建议使用单例开发,struts2通过类的成员变量接收参数,无法使用单例,只能使用多例。 167 | 168 | - 3.经过实际测试,struts2速度慢,在于使用struts标签,如果使用struts建议使用jstl。 169 | -------------------------------------------------------------------------------- /springmvc/12 springmvc注解开发之包装类型参数绑定.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(12)-springmvc注解开发之包装类型参数绑定 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [需求](#需求) 10 | - [实现方法](#实现方法) 11 | - [页面参数和controller方法形参定义](#页面参数和controller方法形参定义) 12 | 13 | 14 | 15 | --- 16 | 17 | 本文主要介绍注解开发的介绍包装类型的参数绑定 18 | 19 | 20 | ## 需求 21 | 22 | 商品查询controller方法中实现商品查询条件传入。 23 | 24 | ## 实现方法 25 | 26 | - 第一种方法:在形参中添加`HttpServletRequest request`参数,通过request接收查询条件参数。 27 | - 第二种方法:在形参中让包装类型的pojo接收查询条件参数。 28 | 29 | 分析: 30 | 31 | 页面传参数的特点:复杂,多样性。条件包括:用户账号、商品编号、订单信息。。。 32 | 33 | 如果将用户账号、商品编号、订单信息等放在简单pojo(属性是简单类型)中,pojo类属性比较多,比较乱。建议使用包装类型的pojo,pojo中属性是pojo。 34 | 35 | ## 页面参数和controller方法形参定义 36 | 37 | - 页面参数: 38 | 39 | 商品名称:`` 40 | 41 | **注意:itemsCustom和包装pojo中的属性名一致即可。** 42 | 43 | 44 | - controller方法形参: 45 | 46 | `public ModelAndView queryItems(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception` 47 | 48 | - 包装类ItemsQueryVo中部分属性: 49 | 50 | ```java 51 | public class ItemsQueryVo { 52 | 53 | //商品信息 54 | private Items items; 55 | 56 | //为了系统 可扩展性,对原始生成的po进行扩展 57 | private ItemsCustom itemsCustom; 58 | ``` 59 | 60 | 可见,`ItemsQueryVo`中属性`itemsCustom`和页面参数中一致 61 | 62 | 63 | -------------------------------------------------------------------------------- /springmvc/13 springmvc注解开发之集合类型参数绑定.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(13)-springmvc注解开发之集合类型参数绑定 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [数组绑定](#数组绑定) 10 | - [需求](#需求) 11 | - [表现层实现](#表现层实现) 12 | - [list绑定](#list绑定) 13 | - [需求](#需求) 14 | - [表现层实现](#表现层实现) 15 | - [map绑定](#map绑定) 16 | 17 | 18 | 19 | --- 20 | 21 | 22 | 本文主要介绍注解开发的集合类型参数绑定,包括数组绑定,list绑定以及map绑定 23 | 24 | ## 数组绑定 25 | 26 | ### 需求 27 | 28 | 商品批量删除,用户在页面选择多个商品,批量删除。 29 | 30 | ### 表现层实现 31 | 32 | 关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。 33 | 34 | - controller方法定义: 35 | 36 | ```java 37 | // 批量删除 商品信息 38 | @RequestMapping("/deleteItems") 39 | public String deleteItems(Integer[] items_id) throws Exception 40 | ``` 41 | 42 | - 页面定义: 43 | 44 | ```jsp 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | 60 | 61 | ## list绑定 62 | 63 | ### 需求 64 | 65 | 通常在需要批量提交数据时,将提交的数据绑定到`list`中,比如:成绩录入(录入多门课成绩,批量提交), 66 | 67 | 本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。 68 | 69 | ### 表现层实现 70 | 71 | - controller方法定义: 72 | - 1、进入批量商品修改页面(页面样式参考商品列表实现) 73 | - 2、批量修改商品提交 74 | 75 | 使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义`list`属性 76 | 77 | ```java 78 | public class ItemsQueryVo { 79 | 80 | //商品信息 81 | private Items items; 82 | 83 | //为了系统 可扩展性,对原始生成的po进行扩展 84 | private ItemsCustom itemsCustom; 85 | 86 | //批量商品信息 87 | private List itemsList; 88 | ``` 89 | 90 | 91 | ```java 92 | // 批量修改商品提交 93 | // 通过ItemsQueryVo接收批量提交的商品信息,将商品信息存储到itemsQueryVo中itemsList属性中。 94 | @RequestMapping("/editItemsAllSubmit") 95 | public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo) throws Exception { 96 | 97 | return "success"; 98 | } 99 | ``` 100 | 101 | - 页面定义: 102 | 103 | ```jsp 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | ``` 115 | 116 | name的格式: 117 | 118 | **`对应包装pojo中的list类型属性名`[`下标(从0开始)`].`包装pojo中List类型的属性中pojo的属性名`** 119 | 120 | 例子: 121 | 122 | `"name="itemsList[${status.index }].price"` 123 | 124 | 125 | *可以和包装类型的参数绑定归纳对比一下,其实就是在包装类的pojo基础上多了个下标。只不过包装类参数绑定时,要和包装pojo中的pojo类性的属性名一致,而list参数绑定时,要和包装pojo中的list类型的属性名一致。* 126 | 127 | 128 | ## map绑定 129 | 130 | 也通过在包装pojo中定义map类型属性。 131 | 132 | 在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。 133 | 134 | - 包装类中定义Map对象如下: 135 | 136 | ```java 137 | Public class QueryVo { 138 | private Map itemInfo = new HashMap(); 139 | //get/set方法.. 140 | } 141 | ``` 142 | 143 | - 页面定义如下: 144 | 145 | ```java 146 | 147 | 148 | 153 | 154 | ``` 155 | 156 | 157 | 158 | - Contrller方法定义如下: 159 | 160 | ```java 161 | public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{ 162 | System.out.println(queryVo.getStudentinfo()); 163 | } 164 | ``` 165 | 166 | 167 | -------------------------------------------------------------------------------- /springmvc/14 springmvc校验.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(14)-springmvc校验 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [校验理解](#校验理解) 10 | - [springmvc校验需求](#springmvc校验需求) 11 | - [环境准备](#环境准备) 12 | - [配置校验器](#配置校验器) 13 | - [添加校验的错误提示信息](#添加校验的错误提示信息) 14 | - [在pojo中添加校验规则](#在pojo中添加校验规则) 15 | - [捕获和显示校验错误信息](#捕获和显示校验错误信息) 16 | - [分组校验](#分组校验) 17 | 18 | 19 | 20 | --- 21 | 22 | 23 | 本文主要介绍springmvc校验,包括环境准备,校验器配置,pojo张添加校验规则,捕获和显示检验错误信息以及分组校验简单示例。 24 | 25 | 26 | ## 校验理解 27 | 28 | 项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。 29 | 30 | 服务端校验: 31 | 32 | - 控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用) 33 | - 业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。 34 | - 持久层dao:一般是不校验的。 35 | 36 | ## springmvc校验需求 37 | 38 | springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。 39 | 40 | 校验思路: 41 | 42 | 页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。 43 | 44 | 具体需求: 45 | 46 | 商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。 47 | 48 | 49 | ## 环境准备 50 | 51 | 我们需要三个jar包: 52 | 53 | - hibernate-validator.jar 54 | - jboss-logging.jar 55 | - validation-api.jar 56 | 57 | 这里我们添加maven依赖 58 | 59 | ```xml 60 | 61 | 62 | org.hibernate 63 | hibernate-validator 64 | 5.2.4.Final 65 | 66 | ``` 67 | 68 | 查看maven依赖树 69 | 70 | ``` 71 | [INFO] \- org.hibernate:hibernate-validator:jar:5.2.4.Final:compile 72 | [INFO] +- javax.validation:validation-api:jar:1.1.0.Final:compile 73 | [INFO] +- org.jboss.logging:jboss-logging:jar:3.2.1.Final:compile 74 | [INFO] \- com.fasterxml:classmate:jar:1.1.0:compile 75 | ``` 76 | 77 | 可以看到,另外两个jar包被`hibernate-validator`依赖,所以不用再额外添加了。 78 | 79 | 80 | ## 配置校验器 81 | 82 | 83 | - 在springmvc.xml中添加 84 | 85 | ```xml 86 | 87 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 97 | 98 | 99 | 100 | classpath:CustomValidationMessages 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | ``` 109 | 110 | - 校验器注入到处理器适配器中 111 | 112 | ```xml 113 | 115 | 116 | ``` 117 | 118 | - 在CustomValidationMessages.properties配置校验错误信息: 119 | 120 | ``` 121 | #添加校验的错误提示信息 122 | items.name.length.error=请输入1到30个字符的商品名称 123 | items.createtime.isNUll=请输入商品的生产日期 124 | ``` 125 | 126 | 127 | ## 在pojo中添加校验规则 128 | 129 | 在ItemsCustom.java中添加校验规则: 130 | 131 | 132 | ```java 133 | public class Items { 134 | private Integer id; 135 | //校验名称在1到30字符中间 136 | //message是提示校验出错显示的信息 137 | //groups:此校验属于哪个分组,groups可以定义多个分组 138 | @Size(min=1,max=30,message="{items.name.length.error}") 139 | private String name; 140 | 141 | private Float price; 142 | 143 | private String pic; 144 | 145 | //非空校验 146 | @NotNull(message="{items.createtime.isNUll}") 147 | private Date createtime; 148 | ``` 149 | 150 | 151 | ## 捕获和显示校验错误信息 152 | 153 | ```java 154 | @RequestMapping("/editItemsSubmit") 155 | public String editItemsSubmit( 156 | Model model, 157 | HttpServletRequest request, 158 | Integer id, 159 | @Validated ItemsCustom itemsCustom, 160 | BindingResult bindingResult)throws Exception { 161 | ``` 162 | 163 | - 在controller中将错误信息传到页面即可 164 | 165 | ``` 166 | //获取校验错误信息 167 | if(bindingResult.hasErrors()){ 168 | // 输出错误信息 169 | List allErrors = bindingResult.getAllErrors(); 170 | 171 | for (ObjectError objectError :allErrors){ 172 | // 输出错误信息 173 | System.out.println(objectError.getDefaultMessage()); 174 | } 175 | // 将错误信息传到页面 176 | model.addAttribute("allErrors", allErrors); 177 | 178 | //可以直接使用model将提交pojo回显到页面 179 | model.addAttribute("items", itemsCustom); 180 | 181 | // 出错重新到商品修改页面 182 | return "items/editItems"; 183 | } 184 | ``` 185 | 186 | - 页面显示错误信息: 187 | 188 | ```jsp 189 | 190 | 191 | 192 | ${ error.defaultMessage}
193 |
194 |
195 | ``` 196 | 197 | ## 分组校验 198 | 199 | - 需求: 200 | - 在pojo中定义校验规则,而pojo是被多个controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验 201 | - 解决方法: 202 | - 定义多个校验分组(其实是一个java接口),分组中定义有哪些规则 203 | - 每个controller方法使用不同的校验分组 204 | 205 | 206 | 207 | 208 | 1.校验分组 209 | 210 | ```java 211 | public interface ValidGroup1 { 212 | //接口中不需要定义任何方法,仅是对不同的校验规则进行分组 213 | //此分组只校验商品名称长度 214 | 215 | } 216 | ``` 217 | 218 | 219 | 2.在校验规则中添加分组 220 | 221 | ```java 222 | //校验名称在1到30字符中间 223 | //message是提示校验出错显示的信息 224 | //groups:此校验属于哪个分组,groups可以定义多个分组 225 | @Size(min=1,max=30,message="{items.name.length.error}",groups = {ValidGroup1.class}) 226 | private String name; 227 | ``` 228 | 229 | 3.在controller方法使用指定分组的校验 230 | 231 | ```java 232 | // value={ValidGroup1.class}指定使用ValidGroup1分组的校验 233 | @RequestMapping("/editItemsSubmit") 234 | public String editItemsSubmit( 235 | Model model, 236 | HttpServletRequest request, 237 | Integer id, 238 | @Validated(value = ValidGroup1.class)ItemsCustom itemsCustom, 239 | BindingResult bindingResult)throws Exception { 240 | ``` 241 | 242 | 243 | -------------------------------------------------------------------------------- /springmvc/15 数据回显.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(15)-数据回显 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [pojo数据回显方法](#pojo数据回显方法) 10 | - [简单类型数据回显](#简单类型数据回显) 11 | 12 | 13 | 14 | --- 15 | 16 | 17 | 18 | 本文介绍springmvc中数据回显的几种实现方法 19 | 20 | 21 | 数据回显:提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。 22 | 23 | 24 | ## pojo数据回显方法 25 | 26 | 1.springmvc默认对pojo数据进行回显。 27 | 28 | **pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)** 29 | 30 | 使用`@ModelAttribute`指定pojo回显到页面在request中的key 31 | 32 | 2.`@ModelAttribute`还可以将方法的返回值传到页面 33 | 34 | 在商品查询列表页面,通过商品类型查询商品信息。在controller中定义商品类型查询方法,最终将商品类型传到页面。 35 | 36 | ```java 37 | // 商品分类 38 | //itemtypes表示最终将方法返回值放在request中的key 39 | @ModelAttribute("itemtypes") 40 | public Map getItemTypes() { 41 | 42 | Map itemTypes = new HashMap(); 43 | itemTypes.put("101", "数码"); 44 | itemTypes.put("102", "母婴"); 45 | 46 | return itemTypes; 47 | } 48 | ``` 49 | 50 | 页面上可以得到itemTypes数据。 51 | 52 | 53 | ```jsp 54 |
63 | ``` 64 | 65 | 3.使用最简单方法使用model,可以不用`@ModelAttribute` 66 | 67 | ```java 68 | //可以直接使用model将提交pojo回显到页面 69 | //model.addAttribute("items", itemsCustom); 70 | ``` 71 | 72 | 73 | ## 简单类型数据回显 74 | 75 | 使用最简单方法使用model 76 | 77 | `model.addAttribute("id", id);` 78 | 79 | 80 | -------------------------------------------------------------------------------- /springmvc/1533949939501.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/1533949939501.png -------------------------------------------------------------------------------- /springmvc/16 异常处理器.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(16)-异常处理器 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [异常处理思路](#异常处理思路) 10 | - [自定义异常类](#自定义异常类) 11 | - [全局异常处理器](#全局异常处理器) 12 | - [错误页面](#错误页面) 13 | - [在springmvc.xml配置全局异常处理器](#在springmvcxml配置全局异常处理器) 14 | - [异常测试](#异常测试) 15 | 16 | 17 | 18 | --- 19 | 20 | 21 | 本文主要介绍springmvc中异常处理的思路,并展示如何自定义异常处理类以及全局异常处理器的配置 22 | 23 | 24 | ## 异常处理思路 25 | 26 | 系统中异常包括两类: 27 | 28 | - 预期异常 29 | - 运行时异常RuntimeException 30 | 31 | 前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。 32 | 33 | 系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图: 34 | 35 | ![springmvc异常处理](http://7xph6d.com1.z0.glb.clouddn.com/springmvc_%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86.png) 36 | 37 | springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。 38 | 39 | 40 | ## 自定义异常类 41 | 42 | 对不同的异常类型定义异常类,继承Exception。 43 | 44 | ```java 45 | package com.iot.learnssm.firstssm.exception; 46 | 47 | /** 48 | * Created by brian on 2016/3/7. 49 | * 50 | * 系统 自定义异常类,针对预期的异常,需要在程序中抛出此类的异常 51 | */ 52 | public class CustomException extends Exception{ 53 | //异常信息 54 | public String message; 55 | 56 | public CustomException(String message){ 57 | super(message); 58 | this.message = message; 59 | } 60 | 61 | public String getMessage() { 62 | return message; 63 | } 64 | 65 | public void setMessage(String message) { 66 | this.message = message; 67 | } 68 | } 69 | ``` 70 | 71 | ## 全局异常处理器 72 | 73 | 思路: 74 | 75 | 系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。 76 | 77 | 全局异常处理器处理思路: 78 | 79 | 解析出异常类型 80 | 81 | - 如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示 82 | - 如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”) 83 | 84 | springmvc提供一个`HandlerExceptionResolver`接口 85 | 86 | 87 | ```java 88 | public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { 89 | //handler就是处理器适配器要执行Handler对象(只有method) 90 | //解析出异常类型 91 | //如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示 92 | //String message = null; 93 | //if(ex instanceof CustomException){ 94 | //message = ((CustomException)ex).getMessage(); 95 | //}else{ 96 | ////如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”) 97 | //message="未知错误"; 98 | //} 99 | 100 | //上边代码变为 101 | CustomException customException; 102 | if(ex instanceof CustomException){ 103 | customException = (CustomException)ex; 104 | }else{ 105 | customException = new CustomException("未知错误"); 106 | } 107 | 108 | //错误信息 109 | String message = customException.getMessage(); 110 | 111 | ModelAndView modelAndView = new ModelAndView(); 112 | 113 | //将错误信息传到页面 114 | modelAndView.addObject("message", message); 115 | 116 | //指向错误页面 117 | modelAndView.setViewName("error"); 118 | 119 | return modelAndView; 120 | 121 | } 122 | } 123 | ``` 124 | 125 | ## 错误页面 126 | 127 | ```jsp 128 | <%-- 129 | Created by IntelliJ IDEA. 130 | User: Brian 131 | Date: 2016/3/4 132 | Time: 10:51 133 | To change this template use File | Settings | File Templates. 134 | --%> 135 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 136 | 137 | 138 | 错误提示 139 | 140 | 141 | ${message} 142 | 143 | 144 | ``` 145 | 146 | ## 在springmvc.xml配置全局异常处理器 147 | 148 | ```xml 149 | 152 | 153 | ``` 154 | 155 | 全局异常处理器只有一个,配置多个也没用。 156 | 157 | 158 | 159 | ## 异常测试 160 | 161 | 在controller、service、dao中任意一处需要手动抛出异常。如果是程序中手动抛出的异常,在错误页面中显示自定义的异常信息,如果不是手动抛出异常说明是一个运行时异常,在错误页面只显示“未知错误”。 162 | 163 | - 在商品修改的controller方法中抛出异常 . 164 | 165 | ```java 166 | public String editItems(Model model,@RequestParam(value="id",required=true) Integer items_id)throws Exception { 167 | 168 | //调用service根据商品id查询商品信息 169 | ItemsCustom itemsCustom = itemsService.findItemsById(items_id); 170 | 171 | //判断商品是否为空,根据id没有查询到商品,抛出异常,提示用户商品信息不存在 172 | if(itemsCustom == null){ 173 | throw new CustomException("修改的商品信息不存在!"); 174 | } 175 | 176 | //通过形参中的model将model数据传到页面 177 | //相当于modelAndView.addObject方法 178 | model.addAttribute("items", itemsCustom); 179 | 180 | return "items/editItems"; 181 | } 182 | ``` 183 | 184 | - 在service接口中抛出异常: 185 | 186 | ```java 187 | public ItemsCustom findItemsById(Integer id) throws Exception { 188 | Items items = itemsMapper.selectByPrimaryKey(id); 189 | if(items==null){ 190 | throw new CustomException("修改的商品信息不存在!"); 191 | } 192 | //中间对商品信息进行业务处理 193 | //.... 194 | //返回ItemsCustom 195 | ItemsCustom itemsCustom = null; 196 | //将items的属性值拷贝到itemsCustom 197 | if(items!=null){ 198 | itemsCustom = new ItemsCustom(); 199 | BeanUtils.copyProperties(items, itemsCustom); 200 | } 201 | 202 | return itemsCustom; 203 | } 204 | ``` 205 | 206 | 207 | - 如果与业务功能相关的异常,建议在service中抛出异常。 208 | - 与业务功能没有关系的异常,建议在controller中抛出。 209 | 210 | 上边的功能,建议在service中抛出异常。 211 | -------------------------------------------------------------------------------- /springmvc/17 上传图片.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(17)-上传图片 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [springmvc中对多部件类型解析](#springmvc中对多部件类型解析) 10 | - [加入上传图片的jar](#加入上传图片的jar) 11 | - [创建图片虚拟目录存储图片](#创建图片虚拟目录存储图片) 12 | - [上传图片代码](#上传图片代码) 13 | 14 | 15 | 16 | --- 17 | 18 | 19 | 本文展示如何在springmvc中上传图片 20 | 21 | 22 | ## springmvc中对多部件类型解析 23 | 24 | 在修改商品页面,添加上传商品图片功能。 25 | 26 | 在页面form中提交`enctype="multipart/form-data"`的数据时,需要springmvc对multipart类型的数据进行解析。 27 | 28 | 在springmvc.xml中配置multipart类型解析器。 29 | 30 | ```xml 31 | 32 | 34 | 35 | 36 | 5242880 37 | 38 | 39 | ``` 40 | 41 | ## 加入上传图片的jar 42 | 43 | 添加依赖 44 | 45 | ```xml 46 | 47 | 48 | commons-fileupload 49 | commons-fileupload 50 | 1.3.1 51 | 52 | ``` 53 | 54 | 依赖树 55 | 56 | ``` 57 | [INFO] \- commons-fileupload:commons-fileupload:jar:1.3.1:compile 58 | [INFO] \- commons-io:commons-io:jar:2.2:compile 59 | ``` 60 | 61 | 可以看到,其实还间接依赖了`commons-io:commons-io:jar` 62 | 63 | 64 | ## 创建图片虚拟目录存储图片 65 | 66 | 参考我之前的博文 67 | 68 | > [在intellij IDEA中为web应用创建图片虚拟目录(详细截图)](http://blog.csdn.net/h3243212/article/details/50819218) 69 | 70 | 71 | 也可以直接修改tomcat的配置,在conf/server.xml文件,添加虚拟目录. 72 | 73 | 注意:在图片虚拟目录中,一定将图片目录分级创建(提高i/o性能),一般我们采用按日期(年、月、日)进行分级创建。 74 | 75 | ## 上传图片代码 76 | 77 | - 页面 78 | 79 | ```jsp 80 | 81 | 82 | 89 | 90 | ``` 91 | 92 | - controller方法 93 | 94 | 修改:商品修改controller方法: 95 | 96 | ```java 97 | @RequestMapping("/editItemsSubmit") 98 | public String editItemsSubmit( 99 | Model model, 100 | HttpServletRequest request, 101 | Integer id, 102 | @ModelAttribute("items") 103 | @Validated(value = ValidGroup1.class)ItemsCustom itemsCustom, 104 | BindingResult bindingResult, 105 | MultipartFile items_pic 106 | )throws Exception { 107 | ``` 108 | 109 | ```java 110 | //原始名称 111 | String originalFilename = items_pic.getOriginalFilename(); 112 | //上传图片 113 | if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){ 114 | 115 | //存储图片的物理路径 116 | String pic_path = "D:\\tmp\\"; 117 | 118 | 119 | //新的图片名称 120 | String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf(".")); 121 | //新图片 122 | File newFile = new File(pic_path+newFileName); 123 | 124 | //将内存中的数据写入磁盘 125 | items_pic.transferTo(newFile); 126 | 127 | //将新图片名称写到itemsCustom中 128 | itemsCustom.setPic(newFileName); 129 | 130 | } 131 | ``` 132 | 133 | 134 | -------------------------------------------------------------------------------- /springmvc/18 json数据交互.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(18)-json数据交互 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [springmvc进行json交互](#springmvc进行json交互) 10 | - [环境准备](#环境准备) 11 | - [添加json转换的依赖](#添加json转换的依赖) 12 | - [配置json转换器](#配置json转换器) 13 | - [json交互测试](#json交互测试) 14 | - [输入json串,输出是json串](#输入json串,输出是json串) 15 | - [输入key/value,输出是json串](#输入keyvalue,输出是json串) 16 | 17 | 18 | 19 | --- 20 | 21 | 22 | 23 | 本文主要介绍如何在springmvc中进行json数据的交互,先是环境准备和配置,然后分别展示了“输入json串,输出是json串”和“输入key/value,输出是json串”两种情况下的交互 24 | 25 | 26 | ## springmvc进行json交互 27 | 28 | json数据格式在接口调用中、html页面中较常用,json格式比较简单,解析还比较方便。 29 | 30 | 比如:webservice接口,传输json数据. 31 | 32 | ![1547948685468](assets/1547948685468.png) 33 | 34 | 35 | 36 | - 请求json、输出json,要求请求的是json串,所以在前端页面中需要将请求的内容转成json,不太方便。 37 | - 请求key/value、输出json。此方法比较常用。 38 | 39 | ## 环境准备 40 | 41 | ### 添加json转换的依赖 42 | 43 | 最开始我少了`jackson-databind`依赖,程序各种报错。 44 | 45 | ```xml 46 | 47 | 48 | 49 | com.fasterxml.jackson.core 50 | jackson-databind 51 | 2.7.2 52 | 53 | 54 | 55 | org.codehaus.jackson 56 | jackson-mapper-asl 57 | 1.9.13 58 | 59 | ``` 60 | 61 | 查看依赖树 62 | 63 | ``` 64 | [INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.7.2:compile 65 | [INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.7.0:compile 66 | [INFO] | \- com.fasterxml.jackson.core:jackson-core:jar:2.7.2:compile 67 | [INFO] \- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.13:compile 68 | [INFO] \- org.codehaus.jackson:jackson-core-asl:jar:1.9.13:compile 69 | ``` 70 | 71 | 72 | ### 配置json转换器 73 | 74 | 在注解适配器中加入`messageConverters` 75 | 76 | ```xml 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | ``` 87 | 88 | **注意:如果使用``则不用定义上边的内容。** 89 | 90 | ## json交互测试 91 | 92 | 显示两个按钮分别测试 93 | 94 | - jsp页面 95 | 96 | ```jsp 97 | <%-- 98 | Created by IntelliJ IDEA. 99 | User: brian 100 | Date: 2016/3/7 101 | Time: 20:49 102 | To change this template use File | Settings | File Templates. 103 | --%> 104 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 105 | 106 | 107 | 108 | json交互测试 109 | 110 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | ``` 123 | 124 | - controller 125 | 126 | 127 | ```java 128 | @Controller 129 | public class JsonTest { 130 | 省略 131 | } 132 | ``` 133 | 134 | - 测试结果 135 | 136 | 137 | ### 输入json串,输出是json串 138 | 139 | 使用jquery的ajax提交json串,对输出的json结果进行解析。 140 | 141 | - jsp页面 142 | 143 | ```jsp 144 | //请求json,输出是json 145 | function requestJson(){ 146 | 147 | $.ajax({ 148 | type:'post', 149 | url:'${pageContext.request.contextPath }/requestJson.action', 150 | contentType:'application/json;charset=utf-8', 151 | //数据格式是json串,商品信息 152 | data:'{"name":"手机","price":999}', 153 | success:function(data){//返回json结果 154 | alert(data); 155 | } 156 | 157 | }); 158 | 159 | } 160 | ``` 161 | 162 | - controller 163 | 164 | ```java 165 | //请求json串(商品信息),输出json(商品信息) 166 | //@RequestBody将请求的商品信息的json串转成itemsCustom对象 167 | //@ResponseBody将itemsCustom转成json输出 168 | @RequestMapping("/requestJson") 169 | public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){ 170 | 171 | //@ResponseBody将itemsCustom转成json输出 172 | return itemsCustom; 173 | } 174 | ``` 175 | 176 | - 测试结果 177 | 178 | ![1547948785389](assets/1547948785389.png) 179 | 180 | 181 | 182 | 可以看到,request和response的HTTP头的Content-Type都是`application/json;charset=utf-8` 183 | 184 | 185 | ### 输入key/value,输出是json串 186 | 187 | 使用jquery的ajax提交key/value串,对输出的json结果进行解析 188 | 189 | - jsp页面 190 | 191 | ```jsp 192 | //请求key/value,输出是json 193 | function responseJson(){ 194 | 195 | $.ajax({ 196 | type:'post', 197 | url:'${pageContext.request.contextPath }/responseJson.action', 198 | //请求是key/value这里不需要指定contentType,因为默认就 是key/value类型 199 | //contentType:'application/json;charset=utf-8', 200 | //数据格式是json串,商品信息 201 | data:'name=手机&price=999', 202 | success:function(data){//返回json结果 203 | alert(data.name); 204 | } 205 | 206 | }); 207 | 208 | } 209 | ``` 210 | 211 | - controller 212 | 213 | 214 | ```java 215 | //请求key/value,输出json 216 | @RequestMapping("/responseJson") 217 | public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){ 218 | 219 | //@ResponseBody将itemsCustom转成json输出 220 | return itemsCustom; 221 | } 222 | ``` 223 | 224 | - 测试结果 225 | 226 | ![1547948785390](assets/1547948785390.png) 227 | 228 | 229 | 230 | 231 | 可以看到,key/value键值对的默认Content-Type是`application/x-www-form-urlencoded`,同时,我们收到了响应“手机” 232 | 233 | 234 | -------------------------------------------------------------------------------- /springmvc/19 RESTful支持.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(19)-RESTful支持 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [概念](#概念) 10 | - [REST的例子](#rest的例子) 11 | - [controller](#controller) 12 | - [REST方法的前端控制器配置](#rest方法的前端控制器配置) 13 | - [对静态资源的解析](#对静态资源的解析) 14 | 15 | 16 | 17 | --- 18 | 19 | 20 | 本文介绍RESTful的概念,并通过一个小例子展示如何编写RESTful风格的controller和配置前端控制器,最后展示静态资源的解析 21 | 22 | 23 | ## 概念 24 | 25 | 首先附上两篇博客链接 26 | 27 | >* [理解RESTful架构 - 阮一峰的网络日志](http://zqpythonic.qiniucdn.com/data/20110912210739/index.html) 28 | >* [RESTful API 设计指南- 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2014/05/restful_api.html) 29 | 30 | 31 | RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。 32 | 33 | RESTful(即Representational State Transfer的缩写)其实是一个开发理念,是对http的很好的诠释。 34 | 35 | 1.对url进行规范,写RESTful格式的url 36 | 37 | - 非REST的url:`http://...../queryItems.action?id=001&type=T01` 38 | - REST的url风格:`http://..../items/001` 39 | 40 | 特点:url简洁,将参数通过url传到服务端 41 | 42 | 2.http的方法规范 43 | 44 | 不管是删除、添加、更新,等等。使用url是一致的,如果进行删除,需要设置http的方法为delete,其他同理。 45 | 46 | 后台controller方法:判断http方法,如果是delete执行删除,如果是post执行添加。 47 | 48 | 3.对http的contentType规范 49 | 50 | 请求时指定contentType,要json数据,设置成json格式的type。 51 | 52 | 53 | ## REST的例子 54 | 55 | 查询商品信息,返回json数据。 56 | 57 | ### controller 58 | 59 | 定义方法,进行url映射使用REST风格的url,将查询商品信息的id传入controller . 60 | 61 | 输出json使用`@ResponseBody`将java对象输出json。 62 | 63 | ```java 64 | //查询商品信息,输出json 65 | //itemsView/{id}里边的{id}表示占位符,通过@PathVariable获取占位符中的参数, 66 | //@PathVariable中名称要和占位符一致,形参名无需和其一致 67 | //如果占位符中的名称和形参名一致,在@PathVariable可以不指定名称 68 | @RequestMapping("/itemsView/{id}") 69 | public @ResponseBody ItemsCustom itemsView(@PathVariable("id") Integer items_id)throws Exception{ 70 | 71 | //调用service查询商品信息 72 | ItemsCustom itemsCustom = itemsService.findItemsById(items_id); 73 | 74 | return itemsCustom; 75 | 76 | } 77 | ``` 78 | 79 | `@RequestMapping(value="/ itemsView/{id}")`:`{×××}`占位符,请求的URL可以是`/viewItems/1`或`/viewItems/2`,通过在方法中使用`@PathVariable`获取{×××}中的×××变量。`@PathVariable`用于将请求URL中的模板变量映射到功能处理方法的参数上。 80 | 81 | 如果`@RequestMapping`中表示为`/itemsView/{id}`,id和形参名称一致,`@PathVariable`不用指定名称。 82 | 83 | 84 | ### REST方法的前端控制器配置 85 | 86 | ```xml 87 | 88 | 89 | springmvc_rest 90 | org.springframework.web.servlet.DispatcherServlet 91 | 92 | 93 | contextConfigLocation 94 | classpath:spring/springmvc.xml 95 | 96 | 97 | 98 | 99 | springmvc_rest 100 | / 101 | 102 | ``` 103 | 104 | 105 | 106 | 107 | 108 | ## 对静态资源的解析 109 | 110 | 配置前端控制器的url-parttern中指定`/`,对静态资源的解析会出现问题,报404错误。 111 | 112 | 113 | 在springmvc.xml中添加静态资源解析方法。 114 | 115 | ```xml 116 | 119 | 120 | ``` 121 | 122 | 这时访问`http://localhost:8080/ssm1/js/jquery-1.4.4.min.js`,可以在浏览器中看到js的内容 123 | -------------------------------------------------------------------------------- /springmvc/22 springmvc开发小结.md: -------------------------------------------------------------------------------- 1 | # springmvc学习笔记(22)-springmvc开发小结 2 | 3 | 标签: springmvc 4 | 5 | --- 6 | 7 | **Contents** 8 | 9 | - [springmvc框架](#springmvc框架) 10 | - [注解开发](#注解开发) 11 | - [使用注解方式的处理器映射器和适配器](#使用注解方式的处理器映射器和适配器) 12 | - [注解开发中参数绑定](#注解开发中参数绑定) 13 | - [springmvc和struts2区别](#springmvc和struts2区别) 14 | - [校验](#校验) 15 | - [数据回显](#数据回显) 16 | - [异常处理](#异常处理) 17 | - [上传图片](#上传图片) 18 | - [json数据交互](#json数据交互) 19 | - [RESTful支持](#restful支持) 20 | - [拦截器](#拦截器) 21 | - [拦截器定义](#拦截器定义) 22 | - [拦截器的配置](#拦截器的配置) 23 | - [拦截器测试及其应用](#拦截器测试及其应用) 24 | 25 | 26 | 27 | --- 28 | 29 | 本文对springmvc系列博文进行小结 30 | 31 | 32 | ## springmvc框架 33 | 34 | ![springmvc_核心架构图](http://7xph6d.com1.z0.glb.clouddn.com/springmvc_%E6%A0%B8%E5%BF%83%E6%9E%B6%E6%9E%84%E5%9B%BE.jpg) 35 | 36 | - `DispatcherServlet`前端控制器:接收request,进行response 37 | - **`HandlerMapping`处理器映射器**:根据url查找Handler。(可以通过xml配置方式,注解方式) 38 | - **`HandlerAdapter`处理器适配器**:根据特定规则去执行Handler,编写Handler时需要按照HandlerAdapter的要求去编写。 39 | - **`Handler`处理器**(后端控制器):需要程序员去编写,**常用注解开发方式**。 40 | - Handler处理器执行后结果是`ModelAndView`,具体开发时`Handler`返回方法值类型包括:`ModelAndView`、`String`(逻辑视图名)、`void`(通过在Handler形参中添加request和response,类似原始 servlet开发方式,注意:可以通过指定response响应的结果类型实现json数据输出) 41 | - `View Resolver`视图解析器:根据逻辑视图名生成真正的视图(在springmvc中使用View对象表示) 42 | - `View`视图:jsp页面,仅是数据展示,没有业务逻辑。 43 | 44 | 45 | 46 | ## 注解开发 47 | 48 | ### 使用注解方式的处理器映射器和适配器 49 | 50 | ```xml 51 | 52 | 53 | 54 | 55 | ``` 56 | 57 | 在实际开发,使用``代替上边处理器映射器和适配器配置。 58 | 59 | - `@controller`注解必须要加,作用标识类是一个Handler处理器。 60 | - `@requestMapping`注解必须要加,作用: 61 | - 1、对url和Handler的**方法**进行映射。 62 | - 2、可以窄化请求映射,设置Handler的根路径,url就是根路径+子路径请求方式 63 | - 3、可以限制http请求的方法 64 | 65 | 66 | 映射成功后,springmvc框架生成一个Handler对象,对象中只包括 一个映射成功的method。 67 | 68 | ### 注解开发中参数绑定 69 | 70 | 将request请求过来的key/value的数据(理解一个串),通过转换(参数绑定的一部分),将key/value串转成形参,将转换后的结果传给形参(整个参数绑定过程)。 71 | 72 | springmvc所支持参数绑定: 73 | 74 | - 1、默认支持很多类型:`HttpServletRequest`、`response`、`session`、`model/modelMap`(将模型数据填充到request域) 75 | - 2、支持简单数据类型,整型、字符串、日期..等 76 | - 只要保证request请求的参数名和形参名称一致,自动绑定成功 77 | - 如果request请求的参数名和形参名称不一致,可以使用`@RequestParam`(指定request请求的参数名),`@RequestParam`加在形参的前边。 78 | - 3、支持pojo类型 79 | - 只要保证request请求的参数名称和pojo中的属性名一致,自动将request请求的参数设置到pojo的属性中。 80 | - 4、包装类型pojo参数绑定 81 | - 第一种方法:在形参中添加`HttpServletRequest request`参数,通过request接收查询条件参数。 82 | - 第二种方法:在形参中让包装类型的pojo接收查询条件参数。 83 | - 5、集合类型参数绑定 84 | - 数组绑定:方法形参使用数组接收页面请求的多个参数 85 | - list绑定:使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list属性 86 | - map绑定:在包装类中定义Map对象,并添加`get/set`方法,action使用包装对象接收 87 | 88 | *注意:形参中即有pojo类型又有简单类型,参数绑定互不影响。* 89 | 90 | 91 | 自定义参数绑定 92 | 93 | - 日期类型绑定自定义: 94 | 95 | 定义的`Converter<源类型,目标类型>`接口实现类,比如:`Converter`,表示:将请求的日期数据串转成java中的日期类型。 96 | 97 | *注意:要转换的目标类型一定和接收的pojo中的属性类型一致。* 98 | 99 | 将定义的Converter实现类注入到处理器适配器中。 100 | 101 | ```xml 102 | 103 | 104 | 105 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | ``` 115 | 116 | 117 | ### springmvc和struts2区别 118 | 119 | springmvc面向方法开发的(更接近service接口的开发方式),struts2面向类开发。 120 | 121 | springmvc可以单例开发,struts2只能是多例开发。 122 | 123 | 124 | ## 校验 125 | 126 | 服务端校验: 127 | 128 | - 控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用) 129 | - 业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。 130 | - 持久层dao:一般是不校验的。 131 | 132 | 一般使用hibernate的校验框架,依赖`hibernate-validator.jar`,`jboss-logging.jar`,`validation-api.jar`这几个jar包 133 | 134 | 开发步骤 135 | 136 | - 在springmvc.xml中添加校验器 137 | - 校验器注入到处理器适配器中 138 | - 在CustomValidationMessages.properties配置校验错误信息 139 | - 在pojo中添加校验规则 140 | - 在控制器中对参数注解`@Validated`来捕获和显示校验错误信息 141 | 142 | 分组校验 143 | 144 | - 定义校验分组 145 | - 在校验规则中添加分组 146 | - 在controller方法使用指定分组的校验 147 | 148 | ## 数据回显 149 | 150 | 数据回显有三种方法 151 | 152 | - 1.springmvc默认对pojo数据进行回显。 153 | - pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写) 154 | - 使用`@ModelAttribute`指定pojo回显到页面在request中的key 155 | - 2.`@ModelAttribute`还可以将方法的返回值传到页面 156 | - 3.使用最简单方法使用model,可以不用`@ModelAttribute` 157 | 158 | 159 | ## 异常处理 160 | 161 | 系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。 162 | 163 | springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。 164 | 165 | 166 | 全局异常处理器处理思路: 167 | 168 | 解析出异常类型 169 | 170 | - 如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示 171 | - 如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”) 172 | 173 | 抛出异常的位置 174 | 175 | - 如果与业务功能相关的异常,建议在service中抛出异常。 176 | - 与业务功能没有关系的异常,建议在controller中抛出。 177 | 178 | ## 上传图片 179 | 180 | 开发步骤 181 | 182 | - 在页面form中提交enctype="multipart/form-data"的数据时,需要springmvc对multipart类型的数据进行解析。 183 | - 在springmvc.xml中配置multipart类型解析器 184 | - 加入上传图片的jar:`commons-fileupload` 185 | - 创建图片虚拟目录存储图片 186 | 187 | ## json数据交互 188 | 189 | 两种json数据交互的形式: 190 | 191 | - 请求json、输出json,要求请求的是json串,所以在前端页面中需要将请求的内容转成json,不太方便。 192 | - 请求key/value、输出json。此方法比较常用。 193 | 194 | 需要的依赖: 195 | 196 | - `jackson-databind` 197 | - `jackson-mapper-asl` 198 | 199 | 200 | 201 | 在注解适配器中加入`messageConverters` 202 | 203 | ```xml 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | ``` 214 | 215 | **注意:如果使用``则不用定义上边的内容。** 216 | 217 | 在controller的返回值上加注解`@ResponseBody`来将java对象输出json,返回json格式数据 218 | 219 | 220 | ## RESTful支持 221 | 222 | `@RequestMapping(value="/ itemsView/{id}")`:`{×××}`占位符,请求的URL可以是`/viewItems/1`或`/viewItems/2`,通过在方法中使用`@PathVariable`获取{×××}中的×××变量。`@PathVariable`用于将请求URL中的模板变量映射到功能处理方法的参数上。 223 | 224 | 如果`@RequestMapping`中表示为`/itemsView/{id}`,id和形参名称一致,`@PathVariable`不用指定名称。 225 | 226 | 同时需要配置前端控制器。若要访问静态资源,还需在springmvc.xml中添加静态资源解析方法,如`` 227 | 228 | ## 拦截器 229 | 230 | ### 拦截器定义 231 | 232 | 定义拦截器,实现`HandlerInterceptor`接口。接口中提供三个方法。可以从名称和参数看出各个接口的顺序和作用 233 | 234 | - `public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception` 235 | - 参数最少,只有三个 236 | - 进入 Handler方法之前执行 237 | - 用于身份认证、身份授权。比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行 238 | - `public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception` 239 | - 多了一个modelAndView参数 240 | - 进入Handler方法之后,返回modelAndView之前执行 241 | - 应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图 242 | - `public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception` 243 | - 多了一个Exception的类型的参数 244 | - 执行Handler完成执行此方法 245 | - 应用场景:统一异常处理,统一日志处理 246 | 247 | ### 拦截器的配置 248 | 249 | - 针对HandlerMapping配置(一般不推荐) 250 | - springmvc拦截器针对HandlerMapping进行拦截设置,如果在某个HandlerMapping中配置拦截,经过该HandlerMapping映射成功的handler最终使用该拦截器 251 | - 类似全局的拦截器 252 | - springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。 253 | 254 | 255 | ### 拦截器测试及其应用 256 | 257 | 链式执行测试 258 | 259 | - 两个拦截器都放行 260 | - preHandle方法按顺序执行,postHandle和afterCompletion按拦截器配置的逆向顺序执行 261 | - 拦截器1放行,拦截器2不放行 262 | - 拦截器1放行,拦截器2 preHandle才会执行。 263 | - 拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。 264 | - 只要有一个拦截器不放行,postHandle不会执行。 265 | - 两个拦截器都不放 266 | - 拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。 267 | - 拦截器1 preHandle不放行,拦截器2不执行。 268 | 269 | 应用 270 | 271 | - 统一日志处理拦截器,需要该拦截器preHandle一定要放行,且将它放在拦截器链接中第一个位置。 272 | - 登陆认证拦截器,放在拦截器链接中第一个位置。权限校验拦截器,放在登陆认证拦截器之后。(因为登陆通过后才校验权限,当然登录认证拦截器要放在统一日志处理拦截器后面) 273 | -------------------------------------------------------------------------------- /springmvc/assets/1536050065921.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1536050065921.png -------------------------------------------------------------------------------- /springmvc/assets/1547948320307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1547948320307.png -------------------------------------------------------------------------------- /springmvc/assets/1547948554726.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1547948554726.png -------------------------------------------------------------------------------- /springmvc/assets/1547948611496.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1547948611496.png -------------------------------------------------------------------------------- /springmvc/assets/1547948632377.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1547948632377.png -------------------------------------------------------------------------------- /springmvc/assets/1547948685468.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1547948685468.png -------------------------------------------------------------------------------- /springmvc/assets/1547948785389.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1547948785389.png -------------------------------------------------------------------------------- /springmvc/assets/1547948785390.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/1547948785390.png -------------------------------------------------------------------------------- /springmvc/assets/Spring-mvc-framework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/Spring-mvc-framework.png -------------------------------------------------------------------------------- /springmvc/assets/spring-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/spring-overview.png -------------------------------------------------------------------------------- /springmvc/assets/springmvc_核心架构图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frank-lam/SpringMVC_MyBatis_Learning/0cbd2fad8a33201391a9e8e5151a6e96127e30e9/springmvc/assets/springmvc_核心架构图.jpg --------------------------------------------------------------------------------
商品名称
商品价格
${item.name }${item.price }${item.detail }修改
"/>
学生信息: 149 | 姓名: 150 | 年龄: 151 | .. .. .. 152 |
55 | 商品名称: 56 | 商品类型: 57 | 62 |
商品图片 83 | 84 | 85 |
86 |
87 | 88 |