目录
1 三大框架SSM
1.1 企业级架构
1.1.1 框架图
1.2 Maven 项目构建工具
1.2.1 概述
1.2.2 为何需要maven?
1.3 四大特征
1.3.1 仓库 repository
1.3.2 依赖 dependency
1.3.3 坐标 coordinate
1.3.4 命令 mvn cmd
1.3.5 小结
1.4 安装
1.4.1 官网
1.4.1 安装
1.5 配置 settings.xml
1.5.1 配置文件
1.5.2 设置镜像仓库
1.5.3 改变仓库位置
1.6 eclipse 集成 maven
1.6.1 配置
1.7 eclipse创建Maven项目
1.7.1 创建maven project
1.7.2 工程目录结构
1.7.3 pom.xml 文件
1.8 IDEA 集成 maven
1.9 IDEA创建maven项目
1.10 添加依赖包
1.10.1 jdbc
1.10.2 添加 mysql
1.11 常见问题
1.11.1 搜索maven依赖的坐标
1.11.2 执行maven命令时内存溢出
1.11.3 版权导致jar远程仓库中没有
1.11.4 下载中断
1.11.5 避免上网下载这么多jar
1.11.6 Maven仓库死活不对之绝招
1.11.7 版本号共享
1.12 SpringBoot介绍
1.12.1 定义
1.12.2 特点
1.13 SpringBoot全新的pom.xml
1.13.1 pom.xml
1.14 eclipse第一个SpringBoot Project
1.14.1 创建maven project
1.14.2 Update Maven
1.14.3 修改pom.xml
1.14.4 创建RunApp.java
1.14.5 创建HelloController.java
1.14.6 启动Tomcat WebServer
1.14.7 测试
1.15 IDEA第一个SpringBootProject
1.15.1 创建SpringBoot project
1.15.2 配置maven
1.15.3 创建RunApp.java
1.15.4 创建HelloController.java
1.15.5 测试
1.16 扩展
1.16.1 执行过程
1.16.2 常见问题:8080端口冲突
1.17 Apache Tomcat
1.17.1 Web中间件历史
1.17.2 servlet-api.jar
1.18 Git忽略特殊目录和文件
1.18.1 .gitignore
1.19 扩展:请求响应过程
1.19.1 HTTP协议
1.19.2 执行过程
1.19.3 谷歌浏览器查看请求响应信息
1.19.4 头信息
1.19.5 响应头
1.19.6 请求头
1.20 SpringMVC 框架介绍
1.20.1 概述
1.20.2 MVC模型
1.20.3 性能超群
1.20.4 工作原理
1.21 案例:展示汽车数据
1.21.1 需求
1.21.2 创建Maven module
1.21.3 创建RunApp.java
1.21.4 Car.java
1.21.5 CarController.java
1.21.6 测试
1.22 处理请求参数
1.22.1 概述
1.22.2 GET方式
1.22.3 POST方式
1.22.4 RESTFul方式(推荐)
1.22.5 测试
1.22.6 前后端关联
1.23 案例:学员信息表单
1.23.1 架构图
1.23.2 项目结构
1.23.3 接收参数
1.23.4 准备stuform.html
1.23.5 准备Student.java
1.23.6 准备StuController.java
1.23.7 日期数据的处理
1.24 扩展
1.24.1 响应
1.24.2 redirect 重定向
1.24.3 forward 转发
1.24.4 拦截器
1.25 总结
1.25.1 springmvc和struts2比较
1.25.2 MVC和SSM的关系
1.25.3 SpringMVC常用的注解
1.26 初识Spring框架
1.26.1 时代变迁
1.26.2 Spring的野心
1.26.3 官网
1.26.4 框架组成
1.26.5 核心概念
1.26.6 三大核心组件的关系
1.26.7 主要jar组成
1.27 Spring框架两大核心:IoC和DI
1.27.1 概念
1.27.2 IoC
1.27.3 DI
1.28 IoC的XML方式
1.28.1 创建springboot工程
1.28.2 创建Hello.java
1.28.3 创建applicationContext.xml
1.28.4 创建TestIoC.java
1.28.5 小结
1.29 IoC的注解方式
1.29.1 创建springboot工程
1.29.2 pom.xml
1.29.3 Hello.java
1.29.4 applicationContext.xml
1.29.5 TestIoC.java
1.30 模拟SrpingIoC的实现
1.30.1 概念
1.30.2 开发步骤
1.30.3 Bean.java
1.30.4 SpringContext.java
1.30.5 Hello.java
1.30.6 TestMyIoC.java
1.31 DI依赖注入
1.31.1 创建Maven工程
1.31.2 pom.xml
1.31.3 Dept.java
1.31.4 User.java
1.31.5 applicationContext.xml
1.31.6 TestDI.java
1.32 小结
1.32.1 面试:IoC和DI
1.32.2 自动装配
1.33 模拟SpringDI的底层实现
1.33.1 Student.java
1.33.2 Teacher.java
1.33.3 自定义注解
1.33.4 TestMyDI.java
1.34 AOP面向切面编程
1.34.1 概念
1.34.2 通知的执行顺序
1.34.3 多切面执行顺序
1.35 拦截器
1.35.1 概述
1.35.2 创建拦截器
1.35.3 注册拦截器
1.35.4 测试
1.36 MVC项目
1.36.1 需求
1.36.2 项目结构
1.36.3 创建RunApp.java
1.36.4 创建application.yml文件
1.36.5 创建Car类
1.36.6 创建CarService接口
1.36.7 创建CarServiceImpl实现类
1.36.8 创建CarController类
1.37 MyBatis持久层框架
1.37.1 概念
1.37.2 内部组件结构图
1.37.3 准备数据表
1.38 MyBatis:XML映射方式
1.38.1 Maven工程结构
1.38.2 pom.xml
1.38.3 mybatis-config.xml
1.38.4 User.java
1.38.5 UserMapper.xml
1.38.6 引入 UserMapper.xml
1.38.7 TestMybatis.java
1.39 常见操作
1.39.1 创建Dept.java
1.39.2 创建DeptMapper.xml
1.39.3 修改mybatis-config.xml
1.39.4 创建TestDept.java
1.40 参数解析
1.40.1 别名:alias
1.40.2 参数值:paramterType
1.40.3 返回值:resultType
1.40.4 返回值:resultMap
1.40.5 集合:List
1.40.6 #和$的区别
1.40.7 SQL中有特殊字符
1.41 动态SQL
1.41.1
1.41.2
1.41.3
1.41.4
1.41.5
1.42 MyBatis:接口映射方式
1.42.1 概述
1.42.2 Maven工程结构
1.42.3 创建Emp.java
1.42.4 创建EmpMapper.xml
1.42.5 创建EmpMapper接口
1.42.6 创建测试类
1.43 ResultMap简单使用
1.43.1 概述
1.43.2 案例
1.43.3 自动匹配规范驼峰规则
1.43.4 对象关系
1.43.5 测试
1.44 ResultMap复杂使用
1.44.1 项目结构
1.44.2 pom.xml
1.44.3 准备数据库表
1.44.4 准备实体类
1.44.5 一对一
1.44.6 一对多
1.45 扩展
1.45.1 JDBC和MyBatis的区别?
1.45.2 XML和接口方式的区别?
1.45.3 接口方式怎么找到xml执行的?
1 三大框架SSM
1.1 企业级架构
1.1.1 框架图
说明: C:\Users\tony\AppData\Local\Temp\WeChat Files\a868115015bc115a829ec17da5c4102.png
之前我们关注的是前端的解决方案(涉及到的技术有H5、CSS3、JavaScript,CSS升级为Bootstrap再升级到ElementUI,JavaScript升级到jQuery再升级到Vue+NodeJS)现在开始我们开始关注后端的解决方案,也就是服务器端到底干了什么,哪些技术来支持(SpringBoot、Maven、SpringMVC、Spring、Mybatis)。这样前后端都学习完,整个软件项目所需要的基本技术就全线贯通,就可以自己独立完成企业级项目的开发了。
下面我们来描述一个经典的业务请求过程:前端html页面发起ajax请求(http://localhost:8080/factoryController/findAll),访问SpringMVC框架的Controller控制层,SpringMVC框架解析请求,找到要调用的某个Controller,找到其中的findAll方法,同时把请求提交的参数封装到java对象中。之后Controller层把请求传递给Spring框架的Service业务层,Service层在把请求传递给Mybatis框架的Mapper持久层,Mapper访问MySQL数据库进行数据库表的查询,查询结果返回给Mapper层,Mapper再返回给Service层,Service再返回给Controller层。Controller把java数据转换为json字符串,返回给ajax调用,ajax进行回调并把json字符串转换为js对象,再在页面中就可以通过js/vue解析js对象,最终把数据展现到html页面中。
l 开发工具: 前端采用HBuilderX,而后端采用eclipse/idea
l 项目管理: 前端采用npm、webpack,而后端采用Maven、SpringBoot
l web中间件: 前端采用NodeJS,而后端采用Tomcat
1.2 Maven 项目构建工具
1.2.1 概述
Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.
Maven是跨平台的项目管理工具。作为Apache组织中的一个颇为成功的开源项目,主要服务于基于java平台的项目构建、依赖管理和项目信息管理。无论是小型的开源类库项目,还是大型的企业级应用;无论是传统的瀑布式开发,还是流行的敏捷模式,Maven都能大显身手。
1.2.2 为何需要maven?
Java工程中我们自己去找jar,或者来自官网,或者来自网友的分享,或者来自项目团队的共享,不论何种方式,都需要把jar文件复制到lib目录中,并且buildpath。
Maven改变这种手动维护jar的方式,设计出一套自动维护jar的体系,已经广泛在软件项目中使用,是软件开发人员必须掌握的技术。
全新的设计体系:创先河的发明pom模型,引入了“仓库”、“依赖”、“坐标”和“命令”。
1.3 四大特征
1.3.1 仓库 repository
Maven和我们之前学习的git很类似,其也是分布式架构,它有一个全球仓库,称为中央仓库,全球开发者都可以连接它来自动下载jar包,而无需去厂家官网下载了。都到一个中央仓库下载,中央仓库压力山大,于是全球各地做镜像仓库,如中国就有网易、阿里等镜像仓库。但每次都去外网下载,那不成天光交网费了。Maven当然不会这么做了,它还有本地仓库。下载一次后,不会再次下载的,除非你删除了。
当用户需要某个jar包时,先到本地仓库寻找,没有再去镜像仓库,没有再去中央仓库。中央仓库找到后,并不直接返回到本地仓库,而是保存一份到镜像仓库,镜像仓库返回本地仓库,本地仓库也保存一份,然后返回给调用者。这样设计是不是太精妙了,只需维护中央仓库,其它仓库自行维护。这就是maven的魅力,这种设计思想是我们开发者需要琢磨和借鉴的。
因为其全自动化,中央仓库默认,镜像仓库需要配置,而无需写一句代码。
仓库只解决了jar从哪来来和放在哪里,jar包千千万,我们有jdbc驱动,有junit单元测试,有spring框架,有mybatis等等,那如何去给我们的项目调用呢?
1.3.2 依赖 dependency
每个核心jar包形成一个依赖,maven底层进行它相关的jar的自动导入
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
1.3.3 坐标 coordinate
cn.tedu
分组
jt
构件-项目/模块
0.0.1-SNAPSHOT
版本
provided
compile/test/provided/runtime/system
-
compile,缺省值,适用于所有阶段,会随着项目一起发布。
-
provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。
-
runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。
-
test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。
-
system,类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。
jar
打包方式,
默认jar,还可以是pom、jar、war
为什么要有坐标呢?坐标的本质是什么呢?
Maven世界拥有大量构建,我们需要找一个用来唯一标识一个构建的统一规范。拥有了统一规范,就可以把查找工作交给机器,默认查找jar包。
看完有何感想?还没豁然开朗?坐标不就是形成一套文件存放规则,这样全球不同厂商的jar包都可以保存在maven仓库中,而不会冲突,各自在各自的目录中。哪怕自家的因为版本号的不同,也会放在不同的目录中,也就不会自家引起冲突。
同时最重要的是,拥有了统一规范,拥有了唯一命名,就可以把查找工作交给自动查找到所要的jar包。
这设计水平可见一斑。一套目录规则,就把jar自动化处理变成现实。
1.3.4 命令 mvn cmd
接下来又是一个很牛的设计,这个思想非常值得品味。Maven借鉴前辈定义了一套生命周期。共有3个生命周期:clean、default、site,每个生命周期包含多个阶段phase。这个并没有什么称奇的,而接下来才是厉害的地方。
常用命令:
Ÿ clean 清理
Ÿ compile 编译
Ÿ test 测试
Ÿ site 站点文档
Ÿ package 打包jar、war
Ÿ deploy 部署到私服
Ÿ install 安装jar到本地仓库中
Ÿ run 运行
每个周期中运行一个命令时,在这个周期里的其他在该命令之前的phase步骤都会执行。如:执行install会自动执行compile(编译java变成了class),test(运行所有单元测试类),package(把整个项目零碎的class文件打包为jar包),最终把成品jar发布到本地仓库中。但执行install并不会自动执行clean。
这意味着什么呢?看看下面命令:
mvn compile、
mvn compile、test
mvn compile、test、package
mvn compile、test、package、install
这意味着一下执行了很多的命令,也称为一键部署,而这过程都是全自动的,以前的开发者每一步都是自己去做的。
还可以一次执行多个命令,各命令又执行它前面的命令:
mvn clean install
注:这些maven命令可以直接在dos窗口中执行的,但需要配置系统变量MAVEN_HOME,但实际开发中我们常和开发IDE环境集成使用,而很少直接dos使用mvn命令,此处就不做介绍了。
1.3.5 小结
优点:
l Jar管理起来更加轻松,已经被业界广泛采用,springboot就是maven的延伸
l 仓库的独特设计,实现仓库自行维护
l 依赖管理方便很多,把开发人员从手工导包解脱出来
l 坐标体系使不同厂商的文件也井然有序,不会冲突覆盖
l 生命周期对应命令,使一键做完以前手动的n步事情
缺点:
l 下载异常让初学者手足无措,不得不删掉仓库重新下就好了,为什么好了,不知道
l 部分包因为版本问题,需要手工导入
l 引发新的问题,版本冲突:大型项目中jar中依赖其它jar包,会发生你调3.1,我调3.2,臭名昭著的版本冲突问题,如何解决呢?上面方式手工排除,而maven采用就近原则
l 本地仓库日积月累巨大,本人的达到2g,很多低版本的jar已经无用,或者过气的技术的jar
l 大型项目中jar冲突非常厉害,仍需手动排除,而且实现方式很多,没有统一规则。如当年加入dubbo的jar时,那冲突叫做满天飞,项目做完也没很良好的解决。但这点springboot却解决了,maven的失败却早就了今天springboot能大行其道的根本原因。
1.4 安装
1.4.1 官网
http://maven.apache.org/download.html
1.4.1 安装
绿色版,下载解压到指定目录,如:d:/java/maven
1.5 配置 settings.xml
1.5.1 配置文件
apache-maven-3.6.3\conf\settings.xml 全局配置文件
1.5.2 设置镜像仓库
默认从maven官网下载,全球都去下载,又是国外网站,因此速度缓慢。
配置为阿里镜像,速度快,但有时有错,如果有错,删除配置,默认从maven官网下载。配置阿里私服镜像仓库,可以写在中间:
<id>ali</id>
<name>ali Maven</name>
<mirrorOf>*</mirrorOf>
<url>https://maven.aliyun.com/repository/public/</url>
1.5.3 改变仓库位置
默认仓库位置:C:\Users\lpx.m2,建议改变默认仓库位置的配置,否则重装操作系统时,可能会把仓库误删除。
1.6 eclipse 集成 maven
1.6.1 配置
Maven很强大,各大开发工具IDE直接集成,eclipse也不例外。进入eclipse的preferences菜单,选中Maven项,将“Download Artifact Sources”打钩,下载jar包的同时会自动下载器源码,方便开发者查看源码,很贴心的功能。
Eclipse默认已经集成了maven(EMBEDDED),但我们基本不采用,据说有问题,我们习惯自己下载最新版本的maven,自行配置。
选择安装的maven所在根目录,别忘记打钩哦。
1.7 eclipse创建Maven项目
1.7.1 创建maven project
1.7.2 工程目录结构
Maven提倡一个口号:约定优于配置!
约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。
项目代码放main下,测试代码放test下,源代码文件放java下,资源文件放resources下。项目代码管理结构清晰,分工明确,各归其位,便于管理,最终便于程序的自动化。Maven命令能一键执行其核心要点就依赖于此。Maven如此,Spring/SpringBoot亦如此。
注意:Maven的个别骨架archetype并不会完整创建上述目录,而且每个骨架创建的也稍有不同,bug不断,没有关系,我们自己手动创建补齐即可。
1.7.3 pom.xml 文件
Maven工程的特点就是在根目录下多了pom.xml文件
4.0.0
cn.tedu
jk
0.0.1-SNAPSHOT
1.8 IDEA 集成 maven
1.9 IDEA创建maven项目
1.10 添加依赖包
1.10.1 jdbc
利用jdbc我们看看如何通过依赖导入mysql驱动包
package cn.tedu;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class jdbctest {
public static void main(String[] args) throws Exception{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/cgb2103"
+ "?characterEncoding=utf8"
+ "&serverTimezone=Asia/Shanghai";
Connection cn = DriverManager.getConnection(url, "root", "root");
String sql = "select * from user";
PreparedStatement ps = cn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while( rs.next() ) {
for(int i=1; i<=3; i++) {
System.out.print( rs.getString(i)+"\t" );
}
}
}
}
1.10.2 添加 mysql
<?xml version="1.0" encoding="UTF-8"?><project xmlns=“http://maven.apache.org/POM/4.0.0”
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>mavencgb</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 添加mysql依赖,可以支持多个依赖,依赖以坐标体现 -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--要和数据库版本匹配,
数据库是5.X的版本就添加5.X的依赖
数据库是8.X的版本就添加8.X的依赖
-->
<version>5.1.48</version>
</dependency>
</dependencies>
1.11 常见问题
1.11.1 搜索maven依赖的坐标
查询最新版本和坐标 http://search.maven.org/
1.11.2 执行maven命令时内存溢出
在使用maven时,如果报内存溢出,如使用 mvn site会耗费大量内存,则修改默认配置。
D:\javaenv\apache-maven-3.0.5\bin\mvn.cmd
在@REM set MAVEN_OPTS=……后加入
set MAVEN_OPTS= -Xms128m -Xmx512m
1.11.3 版权导致jar远程仓库中没有
例如oracle、全文检索的IKAnalyzer分词器、Dubbox等。
解决办法:按maven坐标手动创建目录结构,将jar单独下载,放入其中。
1.11.4 下载中断
远程仓库为国外网站,又是众矢之的,全球都到哪里下载。常会因为网络故障导致jar下载不完全:
jsp-api-2.1.jar.lastUpdated --没下载全,不能用,用时会报错
mysql-connector-java-5.1.48.jar –下载OK的,才可以用
遇到这样的情况:
Ÿ 可以等待网络比较好的时候再下载
Ÿ 可以拷贝别人的仓库
Ÿ 如果只是个别jar包,可以到jar的官网下载后,然后手动配置
最恶劣的一种情况,下载出异常,也就是pom.xml会提示jar包有问题,可到maven本地仓库,jar也存在。这时可以打开jar包,看是否能打开。如果打不开,则删除,触发maven重新下载。
1.11.5 避免上网下载这么多jar
注意: Maven不同的myeclipse/eclipse,myclipse的maven的插件会调用不同版本的jar。不会缺少业务使用的jar。
Maven命令实际是一个jar包,运行前必须需下载maven的插件,运行时判断如果不存在会自动下载。
1.11.6 Maven仓库死活不对之绝招
拷贝环境没问题的 同学的配置文件 和 仓库 。
1.11.7 版本号共享
通常在项目中,我们会同时依赖同一个构件的不同模块,如 spring-orm-3.2.0,spring-context-3.2.0,且多个模块版本相同,为了维护和升级方便,我们可以对其同一管理,这时可以使用到 Maven 属性,类似于变量的概念。
<junit.version>4.10</junit.version>
<spring.version>4.1.3.RELEASE</spring.version>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
命令插件
一键部署,执行每个命令会自动调用前面的命令。可以一次执行多个命名。只能执行本生命周期中的前面的命令。
每个maven命令就是一个jar,一个maven插件。在第一次运行时下载。
1.12 SpringBoot介绍
1.12.1 定义
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。Spring Boot 现在已经成为Java 开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成。成为SpringBoot全家桶。说明: 微信图片_20180622172424
1.12.2 特点
l 创建独立的Spring应用程序
l 嵌入的Tomcat,无需部署WAR文件
l 简化Maven配置
l 自动配置Spring
l 提供生产就绪型功能,如指标,健康检查和外部配置
1.13 SpringBoot全新的pom.xml
1.13.1 pom.xml
它产生整个项目目录,其实我们只需pom.xml文件,覆盖本地maven工程的pom.xml文件。
注:可以使用idea直接创建springboot项目,其本质也是去调用上面的网站,只不过在本地访问外网https://start.spring.io。
修改pom.xml后,把项目进行maven-update 操作,下载依赖的jar包。
<?xml version="1.0" encoding="UTF-8"?><project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>jk</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jk</name>
<description>Demo project for Spring Boot</description>
<!-- 提供相关的 Maven 默认依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<!-- 使用jdk8 -->
<java.version>1.8</java.version>
</properties>
<!-- 添加依赖 -->
<dependencies>
<!-- 使用springmvc构建程序并使用默认的嵌入式容器Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 使用Junit测试springboot程序 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- 添加插件 -->
<plugins>
<!-- springboot添加maven插件,会打包成一个可以直接运行的 JAR 文件,直接部署到服务器 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.14 eclipse第一个SpringBoot Project
1.14.1 创建maven project
1.14.2 Update Maven
默认jdk1.5,早已经过气,改成jdk1.8
1.14.3 修改pom.xml
添加SpringBoot的依赖包
4.0.0
cn.tedu
jkboot
0.0.1-SNAPSHOT
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.14.4 创建RunApp.java
SpringBoot使用了全新的运行方式,两句话,直接执行main方法,默认启动tomcat中间件,端口为8080。
注意:类必须放在其他类的父级目录,它底层使用了包扫描机制,扫描的要求只扫描本类所在的目录极其子目录。
package cn.tedu.jk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication //标识为springboot项目
public class RunApp {
public static void main(String[] args) {
//全新的启动方式,执行run方法会自动启动集成的tomcat中间件
SpringApplication.run(RunApp.class, args);
}
}
1.14.5 创建HelloController.java
目前市场主流已经采用注解方式进行开发
package cn.tedu.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController//接受浏览器的请求 并返回json数据
public class HelloController {
// @RequestMapping(“hi”)//只匹配一个路径
@RequestMapping({"hi","hello/hi"})//匹配两种路径/hi或者/hello/hi
public String hi() {
return "hi springboot!";
}
}
1.14.6 启动Tomcat WebServer
ApacheTomcat是一个非常著名的web中间件,于之前学习的技术的不同在于它提供了web程序的容器环境,专门用于网站服务。
SpringBoot默认集成了tomcat中间件,会自动把项目打包jar发布到tomcat,这样我们就形成了自己的网站。但这个网站只能局域网访问,如果外网(广域网),自己购买一个网站,部署上去即可。
注意:不用服务时,关闭tomcat服务
1.14.7 测试
http://localhost:8080/hello/hi # 某个web服务的根请求
http://localhost:8080/hi # /hi的某个具体请求
url解析:
l localhost代表本机,等价于ip:127.0.0.1
l 8080代表本机的服务,tomcat默认端口8080
l /hi等请求地址在一个web服务中请求具有唯一性(多个就没法区分了)
1.15 IDEA第一个SpringBoot Project
1.15.1 创建SpringBoot project
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621169951(1).png
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621170053(1).png
1.15.2 配置maven
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621170416(1).png
1.15.3 创建RunApp.java
package cn.tedu.cotroller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
@SpringBootApplication
@Controller
public class RunApp {
public static void main(String[] args) {
SpringApplication.run(RunApp.class, args);
}
}
1.15.4 创建HelloController.java
package cn.tedu.cotroller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController//接受浏览器的请求 并返回json数据
public class HelloController {
// @RequestMapping("hi")//只匹配一个路径
@RequestMapping({"hi","hello/hi"})//匹配两种路径/hi或者/hello/hi
public String hi() {
return "hi springboot!";
}
}
1.15.5 测试
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621171469(1).png
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621171607(1).png
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621171655(1).png
1.16 扩展
1.16.1 执行过程
url的解析执行过程:
浏览器输入的url,就会被解析后传入web中间件(/hi),web中间件再会进一步的解析,找到对应的Controller的某个注解里对应的url @RequestMapping({"/","/hi"}) ,从而就找到HelloController的hi()方法。最终利用反射执行这个类的方法(将来这个方法中就用来编写实现业务逻辑代码)。因为使用了@RestController,返回的String字符串对象底层会使用jackson工具类转换java对象为json字符串,返回给浏览器,浏览器将字符串回显(展示)在页面上。
1.16.2 常见问题:8080端口冲突
Tomcat服务不会自动关闭,需要手动关闭,如不关闭,下次启动就会报下面错误:
port 8080 was already in use. 端口8080已经被占用。关闭服务,重新启动即可。
仍未解决的话,以下三种方式任选一个来测试.
1,需要执行DOS命令处理:
C:\Users\lpx>netstat –ano --查看所有端口号,找到8080端口对应的PID号
C:\Users\lpx>taskkill -pid 688 –f --杀死688进程号
2,可能是因为电脑里安装过Oracle数据库:
要找到Oracle的服务OracleXETNSListener,停掉.
3,直接使用yml方式配置修改端口号:
在src/main/resources下添加application.yml文件,内容如下:
server:
port: 8090
清除项目缓存,再次重启服务,测试即可: http://localhost:8090/hello/hi
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621171985(1).png
1.17 Apache Tomcat
1.17.1 Web中间件历史
最早的网站技术是Apache,但它只支持html。当微软推出asp技术后,第一代动态网站技术诞生,微软同时推出了ISS以支持asp技术。随着sun公司的j2ee架构横空出世,提出第二代动态网站技术规范servlet、jsp后,servlet容器就如雨后春笋。Tomcat、Jetty、GlassFish、JBoss、WebSpare、WebLogic,后三者非常厉害,性能超群,但由于昂贵的费用及微服务新架构的兴起,而开始没落,Jetty一直号称性能远胜Tomcat,但Tomcat的霸主地位已经难以撼动(就像编程语言中的java)。目前市场主流都采用Tomcat,特别国内一线互联网企业,而国企采用WebSpare和WebLogic居多。
1.17.2 servlet-api.jar
我们按sun公司的java规范写的servlet(HttpServlet)就专门支持网站的请求request(HttpServletRequest)和响应response(HttpServletResponse)的处理。
各大web中间件厂商按照servlet规范做自己的实现如tomcat的servlet-api.jar。
注:我们的关注点在于实现项目,这里会使用tomcat,初步了解底层结构,不必过渡深入研究。
1.18 Git忽略特殊目录和文件
1.18.1 .gitignore
在git工作空间的根目录下创建文本文件 .gitignore git忽略,配置如下:
.gitignore
*.class
#maven ignore
target/*
#eclipse ignore
.settings/
.project
.classpath
注意:target临时目录配置了不生效
git rm -r --cached . #把本地缓存删除(改变成未track状态),然后再提交
通过变通方式暂时解决:配置 *.class,禁止上传class文件
1.19 扩展:请求响应过程
1.19.1 HTTP协议
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议。HTTP是一个基于TCP/IP通信协议来传递数据(HTML文件、图片文件、查询结果等)。
1.19.2 执行过程
怎么浏览器输入url就可以找到我们写的controller进行执行并且返回呢?
其实这底层是有一套标准的,这个标准称为HTTP协议。我们浏览器成输入的http://这就代表执行http协议。
1.19.3 谷歌浏览器查看请求响应信息
1.19.4 头信息
1.19.5 响应头
1.19.6 请求头
1.20 SpringMVC 框架介绍
1.20.1 概述
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一 般不用),Struts2(一般老项目使用)等。
SpringMVC就是基于MVC设计模式来实现的。
我们的POJO就是Model层,我们的JSP就是视图层,我们的Controller就是控制层。
现在主流基于SSM三大框架开发都是在MVC上继续演化,又分为持久层DAO,业务层Servie,控制层Controller。持久层用来和数据库读写ORM,业务层用来处理复杂的业务逻辑,控制层用来处理MVC的控制。
1.20.2 MVC模型
用来进行分层的结构,这样代码分离结构清晰,各层代码,各司其职,易于开发大型项目。
MVC(Model模型、View视图、Control控制层),将软件进行分层达到松耦合的效果。
通用的软件编程思想, 在MVC设计模式中认为, 任何软件都可以分三层:控制层(Controller)、数据处理模型(Model)、负责展示数据的视图(View)。
在MVC设计思想中要求一个符合MVC设计思想的软件应该保证上面这三部分相互独立,互不干扰,每一个部分只负责自己擅长的部分。如果某一个模块发生变化,应该尽量做到不影响其他两个模块。提高代码的可读性,实现程序间的松耦合、提高代码复用性。
说明: F:\1-work-2020\02-web\liupx\mvc.jpg
1.20.3 性能超群
简单易用性能佳
1.20.4 工作原理
过程简单描述:
客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler-> HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象->前端控制器 DispatcherServlet 渲染数据(Moder)->将得到视图对象返回给用户.
更具体一些的描述:
1、用户发送请求至前端控制器DispatcherServlet。
2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter处理器适配器。
5、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、Controller执行完成返回ModelAndView。
7、andlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
1.21 案例:展示汽车数据
从springmvc2.5开始引入注解方式,特别到了3.0就全面引入注解方式,号称xml零配置。spring3.0配置注解引入后也就是这个点成为了它和struts2的分水岭。随着springmvc的成熟,struts2开始落幕,趋于被市场淘汰。
那下面我们就来体验下:
1.21.1 需求
访问链接: http://localhost:8080/car/get
得到JSON数据: {“id”:718,“name”:“保时捷”,“type”:“Cayman T”,“color”:“红色”,“price”:641000.0}
1.21.2 创建Maven module
1.21.3 创建RunApp.java
package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
@SpringBootApplication
@Controller
public class RunApp {
public static void main(String[] args) {
SpringApplication.run(RunApp.class);
}
}
1.21.4 Car.java
package cn.tedu.pojo;
//Model对象,也称为POJO
//保时捷718 Cayman T,红色,641000元起
public class Car {
private Integer id; //718
private String name; //保时捷
private String type; //Cayman T
private String color; //红色
private Double price; //641000
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Car [id=" + id + ", name=" + name + ", type=" + type + ", color=" + color + ", price=" + price + "]";
}
}
1.21.5 CarController.java
package cn.tedu.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.tedu.pojo.Car;
@RestController
@RequestMapping("/car")
public class CarController {
@RequestMapping("/get")
public Car get() {
//保时捷718 Cayman T,红色,641000元起
Car car = new Car();
car.setId(718);
car.setName("保时捷");
car.setType("Cayman T");
car.setColor("红色");
car.setPrice(641000.0);
return car;
}
}
1.21.6 测试
http://localhost:8080/car/get
执行结果:
{“id”:718,“name”:“保时捷”,“type”:“Cayman T”,“color”:“红色”,“price”:641000.0}
1.22 处理请求参数
1.22.1 概述
当客户端打开浏览器要访问服务器时,可能会带着一些http请求参数过来.
这时,服务器需要获取http参数进行业务处理,如何处理http请求并获取参数呢?
总共有8种,重点时两种方式:GET方式和POST方式.
1.22.2 GET方式
向特定的资源发出请求,并返回实体.有固定的写法.而且数据有最大长度,超出就不行
例如: http://localhost:8080/car/insert?id=1&name=张三&age=18
1.22.3 POST方式
向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
1.22.4 RESTFul方式(推荐)
为了简化GET请求的写法,可以使用RESTFul方式,用法:
1、需要使用注解@PathVariable来获取请求路径中的参数值,@PathVariable用来绑定值
2、通过{???}获取路径中传递来的值
3、以前GET的访问方式即将被简化成:
http://localhost:8080/car/insert/1/张三/18
1.22.5 测试
如果页面的名称和后台形参的名称不一致,可以使用@RequestParam(“页面名称”),就必须指定值.
package cn.tedu.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.tedu.pojo.Car;
@RestController//接受请求,返回json数据
@RequestMapping("/car/")
public class CarController {
//2,通过GET请求,传入参数并接收处理
//访问 http://localhost:8080/car/add?id=10,必须设置id的值否则报错,?拼接是固定语法
@RequestMapping("add")
public void add(int id) {
System.out.println("数据添加成功,id="+id);
}
//访问 http://localhost:8080/car/save?id=90&name=tony ,设置多个参数值时用&连接
@RequestMapping("save")
public void save(int id,String name) {
System.out.println("数据保存成功,id="+id+",name="+name);
}
//访问 http://localhost:8080/car/obj?id=100&name=BMW&color=red
@RequestMapping("obj")
public void obj(Car c) {//处理一个对象的参数
System.out.println(c);
}
//3,优化GET传参的restful方式
//GET方式访问: http://localhost:8080/car/insert?id=1&name=张三&age=18
//restful方式访问: http://localhost:8080/car/insert/1/张三/18
@RequestMapping("insert/{x}/{y}/{z}")
//restful配合@PathVariable注解一起用,使用{资源名}获取传过来的值
public void insert(@PathVariable int x,
@PathVariable String y,
@PathVariable int z) {
System.out.println("数据插入成功,id="+x+",name="+y+",age="+z);
}
}
1.22.6 前后端关联
<head>
<meta charset="UTF-8">
<title>前后端关联</title>
</head>
<body>
<a href="http://localhost:8080/car/restful/3/1">点我</a>
</body>
1.23 案例:学员信息表单
1.23.1 架构图
激动吧,前行吧,终于我们要学一种常规的方式,和ajax不同,它是表单自身提供的一种方式,可以实现前台请求提交给后台系统,经过后台系统处理后,进行展现。
1.23.2 项目结构
1.23.3 接收参数
l 处理一个参数,比如:id
l 处理多个参数,比如:id,name,hobby
l 处理对象,比如:Student数据
l url的RESTFul形式
1.23.4 准备stuform.html
<meta charset="utf-8">
<title>stuform</title>
<style>
input[type='text']{
width: 300px;
height: 20px;
}
</style>
<table style="margin: 30px;">
<h2 style="padding-left: 100px;">学生管理系统</h2>
<tr>
<td>姓名:</td>
</tr>
<tr>
<td>
<input type="text" name="name" placeholder="请输入姓名..."/>
</td>
</tr>
<tr>
<td>年龄:</td>
</tr>
<tr>
<td>
<input type="text" name="age" placeholder="请输入年龄..." />
</td>
</tr>
<tr>
<td>
性别:(单选框)
<input type="radio" name="sex" checked="checked" value="0"/>男
<input type="radio" name="sex" value="1" />女
</td>
</tr>
<tr>
<td>
爱好:(多选)
<input type="checkbox" name="hobby" checked="checked" value="ppq"/>乒乓球
<input type="checkbox" name="hobby" value="ps"/>爬山
<input type="checkbox" name="hobby" value="cg"/>唱歌
</td>
</tr>
<tr>
<td>
学历:(下拉框)
<select name="edu">
<option value ="1">本科</option>
<option value ="2">专科</option>
<option value ="3">研究生</option>
</select>
</td>
</tr>
<tr>
<td>
入学日期:
</td>
</tr>
<tr>
<td>
<input type="date" name="intime"/>
</td>
</tr>
<tr>
<td>
<input type="submit" value="保存" />
<input type="reset" value="取消" />
</td>
</tr>
</table>
1.23.5 准备Student.java
注意:: 日期属性要加注解,@DateTimeFormat(pattern=“yyyy-MM-dd”),否则400错误
package cn.tedu.pojo;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Arrays;
import java.util.Date;
public class Student {private Integer id;
private String name;
private Integer age;
private String sex;
private String[] hobby;
private String edu;
@DateTimeFormat(pattern="yyyy-MM-dd")
//网页上的日期是string,注解用来转换格式,不然400错误
private Date intime;
public Date getIntime() {
return intime;
}
public void setIntime(Date intime) {
this.intime = intime;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public String getEdu() {
return edu;
}
public void setEdu(String edu) {
this.edu = edu;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", hobby=" + Arrays.toString(hobby) +
", edu='" + edu + '\'' +
", intime=" + intime +
'}';
}
}
1.23.6 准备StuController.java
package cn.tedu.controller;
import cn.tedu.pojo.Student;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/stu/")
public class StuController {
//stuform.html的提交路径,会执行此方法的业务
@RequestMapping("add")
public void add(Student s){
System.out.println(s);
}
}
1.23.7 日期数据的处理
把页面上的endtime日期数据,交给后台处理.由于页面的数据都当做String类型处理,所以交给后台处理时,会抛出400错误.需要使用注解进行类型转换.并指定日期格式:
//页面报400 IllegalArgumentException: String->Date
@DateTimeFormat(pattern="yyyy-MM-dd")
private java.util.Date endtime;
public Date getEndtime() {
return endtime;
}
public void setEndtime(Date endtime) {
this.endtime = endtime;
}
1.24 扩展
1.24.1 响应
是指传值到页面的方式
1.24.1.1 利用Model
方式1:model.addAttribute(“personList”, ps.list()); 推荐,清晰
方式2:model.addAttribute(ps.list());
@RequestMapping(value="/home")
public String goHome(Model model){
String message = "springMVC new home!";
model.addAttribute("message", message);
return "index";
}
Model实际上是一个map,在网页调用时,通过视图解析器提交页面前,将map的内容设置到新产生的请求request流中。
1.24.1.2 返回json
@RequestMapping("/json")
@ResponseBody
public List<Person> person(){
List<Person> list = new ArrayList<Person>();
for(int i=0;i<5;i++){
Person p = new Person();
p.setId(i);
p.setName("chen"+i);
list.add(p);
}
return list;
}
1.24.2 redirect 重定向
返回列表页面,通过内部资源视图解析器拼串
return “/person/jPersonList”;
Controller方法返回结果重定向到一个url地址:
return “redirect:/user/userlist.action”;
redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/user/userlist.action后边加参数,如下:
/user/userlist.action?groupid=2&…
1.24.3 forward 转发
controller方法执行后继续执行另一个controller方法。
return “forward:/user/userlist.action”;
forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。
如下例子:
@RequestMapping("/c")
public String c(String groupid,UserVo userVo)throws Exception{
System.out.println("...c...."+groupid+"...user..."+userVo.getUser());
return "forward:/to/d.action";
}
@RequestMapping("/d")
public String d(String groupid,UserVo userVo)throws Exception{
System.out.println("...d...."+groupid+"...user..."+userVo.getUser());
return "success";
}
1.24.4 拦截器
SpringMVC框架自动将配置的拦截器注入到所有的mapping中
拦截器实现性能监控
1.24.4.1 TimeInterceptor
package cn.tedu.springmvc.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class TimeInterceptor implements HandlerInterceptor{
//ThreadLocal解决线程安全问题
private ThreadLocal<Long> tStartTime = new NamedThreadLocal<Long>("tStartTime");
@Override //执行handle前执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Long startTime = System.currentTimeMillis();
tStartTime.set(startTime);
return true;
}
@Override //处理handle后执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
try{
Long stopTime = System.currentTimeMillis();
Long executeTime = stopTime - tStartTime.get();
System.out.println("执行时间:"+executeTime+" 毫秒");
}finally{
tStartTime.remove(); //不释放会引起内存泄漏
}
}
@Override //处理handle完毕后,返回ModelAndView对象前执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
1.24.4.2 拦截配置
<!-- 拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.tedu.springmvc.controller.TimeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
1.25 总结
1.25.1 springmvc和struts2比较
技术
核心分发器
拦截级别
说明
Struts1
DispatcherServlet
类级别
基于Servlet实现
企业中很多旧项目采用的框架
action是单例模式,线程不安全的。
Struts1使用JSTL EL表达式,但是对集合和索引属性的支持很弱。
Struts2
DispatcherFilter
类级别
一个类对应一个request上下文
基于Filter实现
Struts2 action是原型模式 prototype,每次访问对象都会创建新的实例,保证线程安全性;
采用 OGNL解析页面标签。
Struts2是基于松耦合,和web容器脱钩ValueStack
复杂值栈、多例、OGNL导致性能低
安全漏洞频繁,不安全
SpringMVC
DispatcherServlet
方法级别
一个方法对应一个request上下文,而方法同时又跟一个url对应。
基于Servlet实现
Springmvc controller是单例模式,整个程序只有一个对象实例。Spring的安全性是通过绑定threadlocal实现
Spring3 mvc可以认为已经99.9%零配置了。
采用JSTL解析页面标签
基于web容器、单例、JSTL导致性能高
1.25.2 MVC和SSM的关系
1.25.3 SpringMVC常用的注解
@Controller 标识是一个Controller,Spring包扫描创建实例
@RequestMapping 请求后的映射路径
@PathVariable 标识接收单个参数
@ResponseBody 返回对象利用jackson工具类转换为json字符串
@RequestParam 参数名和请求参数名称不同时使用,可以设置默认值
1.26 初识Spring框架
1.26.1 时代变迁
原始时代我们用一个jsp搞定一切,但如此开发大型项目时我们遇到了问题,前端美化的代码和后端的代码交织,代码中又有html、js、css样式,又有业务逻辑和数据库访问代码,杂乱不清晰,美工和开发打架。
于是mvc分层架构封建时代出现,把我们写代码的地方硬性分成3个地方,Model层封装数据,View视图层页面展现,Controller控制层访问转发。代码之间的耦合度降低。概念有了,需要实际干活的。于是随着mvc分层概念的深入人心,业界涌现出很多实现框架,最著名的莫过于struts1和struts2。随着前端框架的成熟,后端框架也应运而生如:dbutils、jdbcTemplate、hibernate、ibatis、mybatis。
一个前端WEB层框架有了,一个后端数据库层访问框架有了,那中间呢?谁来胜任?spring破石而出。
1.26.2 Spring的野心
了解了历史,有个问题值得我们去深思?spring到底想干什么?
它想把全球最好的技术组合到一起,为企业提供高质量的企业级的应用程序框架,减轻开发者开发的难度,减少重复的代码。
目标很宏大,那如何下手呢?如果是你,你会怎么实现?第一步要干什么呢?
我们拿经典的框架来举例子。
struts2作为WEB框架深受企业爱戴,它会自己管理action,来创建其实例,这样在程序中就可以访问action的资源。hibernate作为持久层优秀的框架,它也自己管理持久对象。可以看到,各个诸侯都自己管理对象,而要想让它们对象复用,那真是繁琐。前面就有失败者WebService,为了管理不同的开发语言的对象而层层包装转换,辛苦制定的规则,还借着J2EE规范之名,也推广不开。
如何破局呢?想发号施令,想让人听从,最好的解决办法就是扼住他们的咽喉。在java的世界里最重要的无疑就是对象的生命周期管理。于是spring以此为切入点,实现自己的统治。官宣所有对象由我来管理,struts2你不再管理对象,由我来管理,你要用从我这拿。hibernate你也不再管理对象,由我来管理,你要用从我这拿。你说管就能管的吗?这两个征战数年战功赫赫的大将军会听一个初出茅庐乳臭未干野小子的话?他们当然不会听,spring的话可以不听,但他们都要听开发者的。开发一个完整的系统有四个核心,WEB层支持、业务逻辑层、持久层支持、事务支持。而这就是它们的软肋,这就是它们的命门所在,它们只能完成一部分工作,不是一个整体解决方案。而spring并没有抹杀它们,而是依然给它们高官厚禄,承认它们的市场地位,还赠与一个事务管理。一边打压一边拉拢,它们两位看看大势已去,只能俯首称臣。于是兵不血刃,一场变革悄然兴起,一个经典的三层框架诞生SSH (Strut2+Spring+Hibernate)。
故事很传奇,听的人很开心。可spring真就这么简单吗?如果这样想,你就大错特错了。例如:spring怎么来实现对象的管辖?怎么让不同技术之间能简单的互相配合?这才是spring的决胜之处。
为实现这些spring可是绞尽脑汁、煞费苦心呢。它创新的形成了一套新的理论体系,可谓前无古人后无来者。其中最核心的是:IoC控制反转、DI依赖注入、Bean工厂、SpringAOP面向切面编程、事务控制。
并且spring并没有停止不前,这只是统治地球的第一步,随着spring占领市场后,开始对有功之臣进行清洗,struts2不再优秀,致命bug层出不穷,刚好落井下石,spring推出了springmvc,最终终结了struts2。hibernate想用jdbcTemplate和jdo替代,却被mybatis超越,目前还未统一。世界又达到新的平衡,经典的新三大框架诞生,SSM(SpringMVC+Spring+MyBatis)。Spring并没有放弃,而是另辟蹊径,推出新的产品SpringBoot+SpringCloud 微服务,目前新的趋势已经尘埃落定,一统江湖!
1.26.3 官网
http://spring.io
1.26.4 框架组成
Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。Spring框架的不光是技术牛,而是它的核心思想更牛,它不重复发明轮子,而是“拿来主义”,把业界做的最好的技术黏合起来形成一个强大的企业级的应用框架。
Spring 框架是一个分层架构,由7个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:
说明: Spring(一) 什么是Spring - jiaxiaoyuan1204 - 武陵源的传奇
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
模块
说明
核心容器Spring Core
核心容器提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式,将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring上下文
Spring Context
Spring上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。可以很容易地使 Spring框架管理的任何对象支持AOP。Spring AOP模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO
JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM
Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring Web
Web上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以Spring 框架支持与 Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC框架
MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring 框架的功能可以用在任何J2EE服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同J2EE环境(Web或EJB)、独立应用程序、测试环境之间重用。
Spring以一种非侵入式的方式来管理你的代码,Spring提倡”最少侵入”,这也就意味着你可以适当的时候安装或卸载Spring ,但这点越来越模糊。
1.26.5 核心概念
模块
说明
BeanFactory
Spring内部使用,创建bean的工厂
ApplicationContext
外部应用程序调用,也成为spring容器
IoC控制反转
Inversion of Control
开发者在无需自己new对象,无需关心对象的创建过程
User user = new User(); 手动创建对象
User user = context.getBean(user); 容器创建对象
DI依赖注入
Dependency Injection
松耦合方式实现对象直接的依赖
AOP面向切面编程
补充java面向对象的不足
1.26.6 三大核心组件的关系
Bean、Context、Core三大核心组件的关系:
Bean 包装的是 Object,而 Object 必然有数据,如何给这些数据提供生存环境就是 Context要解决的问题,对 Context 来说它就是要发现每个 Bean 之间的关系,为它们建立这种关系并且要维护好这种关系。所以 Context 就是一个Bean关系的集合,这个关系集合又叫 Ioc 容器,一旦建立起这个 Ioc 容器后 Spring 就可以为你工作了。那 Core 组件又有什么用武之地呢?其实Core 就是发现、建立和维护每个 Bean 之间的关系所需要的一些类的工具,从这个角度看来,Core 这个组件叫 Util 更能让你理解。
把Bean 比作一场演出中的演员的话,那 Context 就是这场演出的舞台背景,而 Core应该就是演出的道具了。只有他们在一起才能具备能演出一场好戏的最基本的条件。当然有最基本的条件还不能使这场演出脱颖而出,还要他表演的节目足够的精彩,这些节目就是 Spring 能提供的特色功能了。
1.26.7 主要jar组成
模块
说明
org.springframework.core
核心工具包,其他包依赖此包
org.springframework.beans
核心,包括:配置文件,创建和管理bean等
org.springframework.aop
面向切面编程,提供AOP的实现
org.springframework.context
提供IoC功能上的扩展服务,此外还提供许多企业级服务的支持,邮件、任务调度、JNDI定位、EJB集成、远程访问、缓存以及多种视图层框架的支持
org.springframework.web.mvc
包含SpringMVC应用开发时所需的核心类
org.springframework.transaction
为JDBC、Hibernate、JDO、JPA提供一致的声明式和编程式事务管理
org.springframework.web
包含Web应用开发时所需支持类
org.springframework.aspects
提供对AspectJ框架的支持
org.springframework.test
对junit等测试框架的简单封装
org.springframework.asm
3.0后提供自己独立的,反编译
org.springframework.context.support
Context的扩展支持,用于mvc方面
org.springframework.expression
Spring表达式语言
org.springframework.instument
对服务器的代理接口
org.springframework.jdbc
对jdbc的简单封装
org.springframework.jms
为简化jms api的使用而做的简单封装
org.springframework.orm
整合第三方orm,如hibernate/mybatis
org.springframework.web.servlet
增强servlet
1.27 Spring框架两大核心:IoC和DI
1.27.1 概念
l IoC(Inversion of Control)简单来说就是将对象Object的创建的权力及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不在需要关注对象的创建和生命周期的管理,而是在需要的时候由Spring框架提供,这个由Spring框架管理对象创建和生命周期的机制称之为控制反转。
l 在创建对象的过程中Spring可以依据对象的关系,自动把其它对象注入(无需创建对象,直接拿着使用)进来,这个过程称之为DI(Dependency Injection)依赖注入。
总结下Spring核心就干了两件事:
-
创建对象
-
设置对象的关联关系
1.27.2 IoC
IOC(Inversion of Control),控制反转。
就是指将对象的创建,对象的存储(map),对象的管理(依赖查找,依赖注入)交给了spring容器。
1.27.3 DI
DI(Dependency Injection)依赖注入 。
相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入,即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。
1.28 IoC的XML方式
1.28.1 创建springboot工程
1.28.2 创建Hello.java
package spring;
public class Hello {
public void hi() {
System.out.println("Hello Spring.");
}
}
1.28.3 创建applicationContext.xml
1.28.4 创建TestIoC.java
package spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIoC {
@Test
public void bean() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Hello h1 = new Hello();
System.out.println(h1);
h1.hi();
//根据id获取bean
//Spring就是一个大工厂(容器)专门生成bean,bean就是对象
Hello hello = (Hello)ac.getBean("hello");
System.out.println(hello);
hello.hi();
}
}
1.28.5 小结
这就是spring框架的IoC,控制反转。之前我们自己new出新类。new User();变成由一个初始化的xml配置文件来创建,也就是由spring容器来创建。遍历xml配置文件,读取到,获取到class属性的类的全路径,利用反射创建这个类。
在java范畴中万物皆Object,在Spring中万物皆Bean。Bean是Spring的核心、基础、根源。
1.29 IoC的注解方式
1.29.1 创建springboot工程
1.29.2 pom.xml
<project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 集中定义依赖版本号 -->
<properties>
<junit.version>4.10</junit.version>
<spring.version>4.1.3.RELEASE</spring.version>
</properties>
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
1.29.3 Hello.java
package spring;
import org.springframework.stereotype.Component;
@Component//让spring容器认识
public class Hello {
public void hi() {
System.out.println("Hello Spring.");
}
}
1.29.4 applicationContext.xml
<beans xmlns=“http://www.springframework.org/schema/beans”
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<context:component-scan base-package="cn.tedu" />
设置属性:property的value
关联对象:property的ref
1.29.5 TestIoC.java
测试类:
package spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIoC {
@Test
public void bean() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据id获取bean,beanname就是类名,首字母变小写
Hello hello = (Hello) applicationContext.getBean("hello");
System.out.println(hello);
hello.hi();
}
}
1.30 模拟SrpingIoC的实现
1.30.1 概念
SpringIoC控制反转底层实现利用了java自身提供的反射技术来创建对象
Class.forName(classPath).newInstance();
通过这种方式,spring控制了对象的生命周期,可以随时自行增强对象,如DI依赖注入,如AOP,环绕通知在类创建前后增强功能,如Transaction事务加强等。
1.30.2 开发步骤
-
创建容器管理bean,并初始化容器-> [user,dept,hello]
-
创建spring容器,并初始化容器-> {hello=new Hello(),user=new Uer() }
-
提供getBean(),根据bean的名字,从spring容器中获取对应的对象
1.30.3 Bean.java
抽象Bean的定义,取代java中的Object,Spring框架中万物皆Bean。
package cn.tedu.design;
//模拟spring管理bean,存放bean
public class Bean {
private String name;//bean名字-> hello
private String path;//bean对应的类路径->cn.tedu.desing.Hello
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
1.30.4 SpringContext.java
逻辑复杂,IoC实现的核心,最关键点还是怎么创建对象实例:
package cn.tedu.design;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
//模拟spring框架,是spring的核心
//1,创建容器管理bean-> [user,dept,hello]
//2,创建spring容器-> {hello=new Hello(),user=new Uer() }
//3,getBean(),有就直接取出来,没有就创建并放入容器
public class SpringContext {
//1,创建容器管理bean-> [user,dept,hello]
private List<Bean> beanFactory = new ArrayList<>();
//初始化容器
public SpringContext() throws Exception {
//创建Bean,并加入容器中
Bean bean = new Bean();
bean.setName("hello");
bean.setPath("cn.tedu.pojo.Hello");
beanFactory.add(bean);
init();
}
//2,创建spring容器-> {hello=new Hello(),user=new Uer() } //并发安全的map
private final Map<String, Object> factoryBeanObject = new ConcurrentHashMap<>();
//初始化spring容器-> {hello=new Hello(),user=new Uer() }
public void init() throws Exception {
//遍历beanFactory,得到每个bean
for(Bean b : beanFactory) {
//map里的key
String key = b.getName();
//反射创建对象,作为value存入map
String path = b.getPath();
Object value = Class.forName(path).newInstance();
factoryBeanObject.put(key, value);
}
}
//3,getBean()有就直接取出来,没有就创建并放入容器
public Object getBean(String name) {
return factoryBeanObject.get(name);//去map里根据key找value
}
}
1.30.5 Hello.java
package spring;
public class Hello {
public void hi() {
System.out.println("hi springioc");
}
}
1.30.6 TestMyIoC.java
package cn.tedu.spring;
import cn.tedu.design.SpringContext;
import cn.tedu.pojo.Hello;
public class TestMyIOC {
public static void main(String[] args) throws Exception {
SpringContext spring = new SpringContext();
Hello o = (Hello)spring.getBean("hello");
System.out.println(o);//cn.tedu.pojo.Hello@6d06d69c
o.hi();
}
}
1.31 DI依赖注入
1.31.1 创建Maven工程
1.31.2 pom.xml
<project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 集中定义依赖版本号 -->
<properties>
<junit.version>4.10</junit.version>
<spring.version>4.1.3.RELEASE</spring.version>
</properties>
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
1.31.3 Dept.java
package spring.pojo;
import org.springframework.stereotype.Component;
@Component
public class Dept {
public String deptName = "软件部";
}
1.31.4 User.java
package spring.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class User {
//相当于框架完成了:new User().setDept(new Dept());
@Autowired
private Dept dept; //对象的关联
public String name = "tony";
}
1.31.5 applicationContext.xml
<beans xmlns=“http://www.springframework.org/schema/beans”
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 扫描包 -->
<context:component-scan base-package="cn.tedu.pojo" />
1.31.6 TestDI.java
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring.pojo.Dept;
import spring.pojo.User;
public class TestDI {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Dept d = (Dept)ac.getBean("dept");
User u = (User)ac.getBean("user");
System.out.println(d);
System.out.println(u);
System.out.println(u.getDept().deptName);
}
}
1.32 小结
1.32.1 面试:IoC和DI
在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时可能需要多个对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。
所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。
IoC是设计思想,IoC有三个核心:BeanFactory、反射、DI。BeanFactory利用反射实现对象的创建,DI实现对象关系管理。
1.32.2 自动装配
利用注解方式,我们只需要写@Autowired注解,底层就会去容器中找对应的对象,如果有获取到,反射调用其对应的set方法,设置。而这个调用过程都是自动,我们没有手工去写set方法。所以这个过程也称为自动装配。
1.33 模拟SpringDI的底层实现
1.33.1 Student.java
package cn.tedu.designdi;
import org.springframework.stereotype.Component;
@Component
public class Student {
private String name="王一博";
@MyAutowired //di
private Teacher teacher;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
}
1.33.2 Teacher.java
package cn.tedu.designdi;
import org.springframework.stereotype.Component;
@Component
public class Teacher {
private String name="皮皮霞";
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
1.33.3 自定义注解
package cn.tedu.designdi;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
String value() default "" ;
}
1.33.4 TestMyDI.java
package cn.tedu.designdi;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.Field;
//有注解的属性就new
public class TestDI {
@Test
public void di() throws Exception{
//获取Student类里的属性,哪个属性有MyAutowired注解就给他new一个
Class s = Class.forName("cn.tedu.designdi.Student");
Object o = s.newInstance();
Field[] fs = s.getDeclaredFields();
for (Field f : fs) {
MyAutowired an = f.getAnnotation(MyAutowired.class);
if(an != null){
f.setAccessible(true);
f.set(o,new Teacher());
}
}
// Student{name=‘王一博’, teacher=Teacher{name=‘皮皮霞’}}
System.out.println((Student)o);
}
}
1.34 AOP面向切面编程
1.34.1 概念
Spring核心特征中除了IoC控制反转、DI依赖注入,还有一个核心就是强大的面向切面编程AOP(Aspect Oriented Programming)的实现。
Sring AOP有三要素:
l Aspect定义切面;
l 通过通知(Advice)来指定具体做什么事情。如方法执行前做什么,方法执行后做什么,抛出异常做什么,从而实现对象行为(方法)的增强;
l 具体通过切点(PointCut)配置切点表达式(expression)来指定在哪些类的哪些方法上织入(ware)横切逻辑;被切的地方叫连接点(JoinPoint);
1.34.2 通知的执行顺序
Spring框架实现了AOP面向切面,其引入了第三方AspectJ框架来具体实现。AspectJ提供了五种切入方式,术语称为通知advice。
具体五种为:
-
前置通知before
-
后置通知after
-
环绕通知around
-
返回后通知afterReturning
-
异常通知afterThrowing。
异常通知特殊,这里暂不讨论。
可以看到,分别在业务方法(Business Method)的执行前后进行拦截,执行指定的代码。
1.34.3 多切面执行顺序
下面是 两个切面 各通知的执行顺序:
1.35 拦截器
1.35.1 概述
Springmvc提供的, 主要用于拦截用户请求并在方法执行前后添加功能.本质上也是AOP的思想. SpringMVC框架自动将配置的拦截器注入到所有的mapping中.
应用场景:
1、日志记录:可以记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等等。
2、权限检查:如登陆检测,进入处理器检测是否登陆,如果没有直接返回到登陆页面。
3、性能监控:可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间;
4、事务控制:对事务的提交回滚等,与业务无关的代码
PreHandle //方法执行前被调用
Method //要执行的方法
PostHandle //方法执行后被调用
AfterCompletion //完全执行完后被调用(如:释放资源)
1.35.2 创建拦截器
package cn.tedu.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//自定义拦截器:在方法执行前后添加与业务无关的功能
@Component//交给spring管理bean的ioc di
public class MyInterceptor implements HandlerInterceptor{
long start ;
long end ;
@Override //在方法执行前调用
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
start = System.currentTimeMillis();
System.out.println("方法要被执行啦~");
return true;//放行
}
@Override //在方法执行后调用
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
end = System.currentTimeMillis();
System.out.println("方法执行完啦~");
}
@Override //都完成后调用
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("都干完啦~耗时: "+(end-start)+"ms");
}
}
1.35.3 注册拦截器
注册拦截器,必须实现接口WebMvcConfigure,通知spring容器管理这个类
package cn.tedu.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration//告诉spring容器,我是一个配置类
public class RegistInterceptor implements WebMvcConfigurer {
@Autowired//自动装配
private MyInterceptor a ;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//把自定义的拦截器,放入容器中
registry.addInterceptor(a);
}
}
1.35.4 测试
说明: C:\Users\ADMINI~1\AppData\Local\Temp\1621522556(1).png
package cn.tedu.interceptor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/in")
public class InController {
@RequestMapping("/get")
public void get(){
System.out.println(111);
}
@RequestMapping("/save")
public void save(){
for (int i = 0; i < 1000; i++) {
System.out.print(i);
}
}
}
1.36 MVC项目
Spring用来管理项目中的所有Bean,需要使用注解@Component @Autowired @Service @Component等
Springmvc用来管理Controller层,需要使用的注解有@RestController @RequestMapping等
1.36.1 需求
访问链接: http://localhost:8080/car/get
得到JSON数据: {“name”:“保时捷”,“color”:“红色”,“price”:641000.0}
1.36.2 项目结构
1.36.3 创建RunApp.java
package cn.tedu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RunApp {
public static void main(String[] args) {
SpringApplication.run(RunApp.class);
}
}
1.36.4 创建application.yml文件
\
server:
port: 8090
1.36.5 创建Car类
package cn.tedu.pojo;
import org.springframework.stereotype.Component;
@Component
public class Car {
private String name ;
private String color;
private double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", price=" + price +
'}';
}
}
1.36.6 创建CarService接口
package cn.tedu.service;
import cn.tedu.pojo.Car;
public interface CarService {
Car get();
}
1.36.7 创建CarServiceImpl实现类
package cn.tedu.service;
import cn.tedu.pojo.Car;
import org.springframework.stereotype.Service;
@Service
public class CarServiceImpl implements CarService{
@Override
public Car get() {
Car c = new Car();
c.setName("保时捷");
c.setColor("红色");
c.setPrice(641000.0);
return c;
}
}
1.36.8 创建CarController类
package cn.tedu.controller;
import cn.tedu.pojo.Car;
import cn.tedu.service.CarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/car/")
public class CarController {
@Autowired
private CarService carService;
@RequestMapping("get")
public Car get(){
return carService.get();
}
}
1.37 MyBatis持久层框架
1.37.1 概念
MyBatis的前身就是iBatis,iBatis本是apache的一个开源项目,2010年5月这个项目由apahce sofeware foundation 迁移到了google code,并且改名为MyBatis。
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
1、 简化JDBC的开发
2、 能够更好的完成ORM(对象关系映射)
1.37.2 内部组件结构图
1.37.3 准备数据表
create database mybatisdb default character set utf8;
use mybatisdb;
create table user(id int primary key auto_increment,name varchar(100),addr varchar(100),age int);
Insert into user values(null,‘hanmeimei’,‘北京’,28);
Insert into user values(null,‘xiongda’,‘上海’,20);
Insert into user values(null,‘xiaonger’,‘上海’,19);
DROP TABLE IF EXISTS dept
;
CREATE TABLE dept
(
id
int(11) NOT NULL AUTO_INCREMENT,
dname
varchar(14) DEFAULT NULL,
loc
varchar(13) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
– Records of dept
INSERT INTO dept
VALUES (‘1’, ‘呵呵呵’, ‘一区’);
INSERT INTO dept
VALUES (‘2’, ‘哈哈哈哈’, ‘二区’);
INSERT INTO dept
VALUES (‘3’, ‘operations’, ‘二区’);
INSERT INTO dept
VALUES (‘5’, ‘java教研部’, ‘大钟寺’);
INSERT INTO dept
VALUES (‘10’, ‘开发’, ‘西二旗’);
DROP TABLE IF EXISTS emp
;
CREATE TABLE emp
(
id
int(11) NOT NULL AUTO_INCREMENT,
ename
varchar(10) DEFAULT NULL,
job
varchar(9) DEFAULT NULL,
mgr
decimal(4,0) DEFAULT NULL,
hiredate
date DEFAULT NULL,
sal
decimal(7,2) DEFAULT NULL,
comm
decimal(7,2) DEFAULT NULL,
deptno
int(11) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=510 DEFAULT CHARSET=utf8;
– Records of emp
INSERT INTO emp
VALUES (‘100’, ‘jack’, ‘副总’, null, ‘2002-05-03’, ‘90000.00’, null, ‘1’);
INSERT INTO emp
VALUES (‘200’, ‘tony’, ‘总监’, ‘100’, ‘2015-02-02’, ‘10000.00’, ‘2000.00’, ‘2’);
INSERT INTO emp
VALUES (‘300’, ‘hana’, ‘经理’, ‘200’, ‘2017-02-02’, ‘8000.00’, ‘1000.00’, ‘2’);
INSERT INTO emp
VALUES (‘400’, ‘leo’, ‘员工’, ‘300’, ‘2019-02-22’, ‘3000.00’, ‘200.12’, ‘2’);
INSERT INTO emp
VALUES (‘500’, ‘liu’, ‘员工’, ‘300’, ‘2019-03-19’, ‘3500.00’, ‘200.58’, ‘2’);
INSERT INTO emp
VALUES (‘502’, ‘王一博’, ‘topidol.’, ‘1000’, ‘2021-03-31’, ‘20000.00’, ‘99.00’, ‘88’);
INSERT INTO emp
VALUES (‘504’, ‘蔡徐坤’, ‘rapper’, ‘10’, ‘2021-03-29’, ‘100.00’, ‘1000.00’, ‘100’);
1.38 MyBatis:XML映射方式
1.38.1 Maven工程结构
注:导入jUnit5,使用@BeforeEach注解替代jUnit4的@Before。
1.38.2 pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatis</name>
<description>Demo project for Spring Boot</description>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
5.1.48
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.38.3 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?><environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
1.38.4 User.java
package cn.tedu.pojo;
public class User {
private int id;
private String name;
private String addr;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", addr=" + addr + ", age=" + age + "]";
}
}
1.38.5 UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?><select id="get" resultType="cn.tedu.pojo.User">
select * from user
</select>
1.38.6 引入 UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?><environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mapper resource="mappers/UserMapper.xml"/>
1.38.7 TestMybatis.java
package cn.tedu.test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import cn.tedu.pojo.User;
public class TestMybatis {
@Test
public void get() throws IOException {
//1,创建SqlSessionFactory对象,线程非安全,用来产生SqlSession
//2,创建SqlSession,用来执行sql
//3, 定位SQL: namespace的值+id的值
//4,解析结果并打印
InputStream in = Resources.getResourceAsStream(“mybatis-config.xml.xml”);
SqlSessionFactory session = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = session.openSession();
List<User> list = sqlSession.selectList("hello.get");
for (User u : list) {
System.out.println(u);
}
}
}
1.39 常见操作
利用Mybatis操作数据库,对数据进行增删改查操作.
1.39.1 创建Dept.java
package cn.tedu.pojo;
public class Dept {
public Dept() {}
public Dept(int id, String dname, String loc) {
super();
this.id = id;
this.dname = dname;
this.loc = loc;
}
private int id;
private String dname;
private String loc;
//其实,springboot已经整合里lombok,可以不用写getters/setters
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
@Override
public String toString() {
return "Dept [id =" + id + ", dname=" + dname + ", loc=" + loc + "]";
}
}
1.39.2 创建DeptMapper.xml
<?xml version="1.0" encoding="UTF-8"?><select id="getAll" resultType="cn.tedu.pojo.Dept">
select * from dept
</select>
<!-- 查一条记录 ,resultType是结果类型意思是要把结果封装给谁 -->
<select id="getOne" resultType="cn.tedu.pojo.Dept">
select * from dept where id =1
</select>
<!-- 动态的 查一条记录 ,resultType是结果类型意思是要把结果封装给谁 ,parameterType是SQL需要的参数x的类型-->
<select id="getOne2" resultType="cn.tedu.pojo.Dept" parameterType="int">
<!-- where 字段名=要设置的值 , ${x} 通过${}来获取传递过来的值-->
select * from dept where id =${x}
</select>
<!-- 查询 总记录数 -->
<select id="getSum" resultType="int">
select count(*) from dept
</select>
<!-- 新增记录 $和#的区别就是#会自动拼接字符串,$只会取到值本身-->
<insert id="add" parameterType="cn.tedu.pojo.Dept">
<!-- Cause: java.sql.SQLSyntaxErrorException: Unknown column 'java教研部' in 'field list' -->
<!-- insert into dept(dname,loc) values(${dname},${loc}) -->
<!-- 获取参数的值时,如果参数是String类型,$只取值loc=tony,不会自动拼串.#可以给String的参数拼串loc='tony' -->
insert into dept(dname,loc) values( #{dname},#{loc} )
</insert>
<!-- 修改 -->
<update id="update" parameterType="String">
<!-- $不会自动拼串鸭 dname=哈哈哈哈 -->
<!-- update dept set dname=${x} where deptno=2 -->
update dept set dname=#{x} where id =2
</update>
<!-- 删除 -->
<delete id="del" parameterType="int">
delete from dept where id =${a}
</delete>
1.39.3 修改mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?><environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/DeptMapper.xml"/>
1.39.4 创建TestDept.java
package cn.tedu.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import cn.tedu.pojo.Dept;
public class TestDept {
private SqlSession session ;
@BeforeEach //执行@Test之前要执行的
public void init() throws IOException {
InputStream in = Resources.getResourceAsStream(“mybatis-config.xml.xml”);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
session = factory.openSession();
}
@Test
public void getAll() {
List<Dept> list = session.selectList("deptm.getAll");
// List list = session.selectList(定位SQL,SQL需要的参数)
for (Dept d : list) {
System.out.println(d);
}
}
@Test
public void getOne() {//执行写死的SQL
Dept d = session.selectOne("deptm.getOne");
System.out.println(d);
}
@Test
public void getOne2() {//执行动态 的SQL
Dept d = session.selectOne("deptm.getOne2",1);
System.out.println(d);
}
@Test
public void getSum() {//查询总记录数
int count = session.selectOne("deptm.getSum");
System.out.println(count);
}
@Test
public void add() {
// session.insert(statement)
Dept dept = new Dept(10,"java教研部","大钟寺");
session.insert("deptm.add", dept);
//更新了数据,必须提交事务,否则数据库刷不出来效果的.查询没事
//Mybatis不会自动提交事务,需要手动提交。提交方式有两种:openSession(true)或者session.commit()
session.commit();
}
@Test
public void update() {
// session.update(statement)
session.update("deptm.update","哈哈哈哈" );
//更改了数据,就要提交事务
session.commit();
}
@Test
public void del() {
session.delete("deptm.del",6);
//更改了数据,就要提交事务
session.commit();
}
}
1.40 参数解析
1.40.1 别名:alias
在sqlMapConfig.xml配置,在映射文件中直接写对象名称即可
1.40.2 参数值:paramterType
指定参数类型,通常制定一个对象类型。
1.40.3 返回值:resultType
非常重要的东西,即完成ORM的映射关系所在。这里指定的cd.tedu.mybatis.domain.User代表把结果集转换成一个User对象实例。
1.40.4 返回值:resultMap
resultMap 用于对复杂对象结构时,对应的ResultMap结构名称
1.40.5 集合:List
在MyBatis中,大多默认采用List集合,声明集合参数或者返回值时,都有个奇怪的写法,本应写List,但习惯只写集合元素的类型:String,大家切记。
1.40.6 #和$的区别
两种方式都可以获取参数的值。区别如下:
(推荐!)#: 使用#{parameterName}引用参数的时候,Mybatis会把这个参数认为是一个字符串,例如传入参数是“Smith”,那么在SQL(Select * from emp where name = #{employeeName})使用的时候就会转换为Select * from emp where name = ‘Smith’。
$: 不做字符串拼接,SQL(Select * from emp where name = ${employeeName})使用的时候就会转换为Select * from emp where name = Smith。此时,如果字段是varchar类型直接抛出SQL异常。
从安全性上考虑,能使用#尽量使用#来传参,因为这样可以有效防止SQL注入的问题。
1.40.7 SQL中有特殊字符
当SQL中有特殊字符,mybatis不能正常解析时,
用<![CDATA[ ?? ]]>括起来就解决了 <![CDATA[ and age<=#{age} ]]>
<![CDATA[ and age<=#{age} ]]>1.41 动态SQL
Mybatis提供使用ognl表达式动态生成sql的功能。
1.41.1
Sql标签用来提取SQL片段,来提高SQL的复用.
使用位置需要通过include引用指定的SQL片段.
id,title,sell_point,price,num,barcode,image,cid,status,created,updated
SELECT FROM tb_item
1.41.2
执行SQL时,可以添加一些判断条件.
SELECT FROM tb_item where
<if test="title != null"> title like #{title} </if>
<if test="sellPoint != null">and sell_point like #{sellPoint}</if>
1.41.3
去掉条件中可能多余的and或者or:
SELECT FROM tb_item
<if test="title != null"> title like #{title} </if>
<if test="sellPoint != null">and sell_point like #{sellPoint}</if>
1.41.4
去掉最后可能多余的逗号:
UPDATE teachers
<if test="tname != null">tname=#{tname},</if>
<if test="tsex != null">tsex=#{tsex},</if>
<if test="tbirthday != null">tbirthday=#{tbirthday},</if>
<if test="prof != null">prof=#{prof},</if>
<if test="depart != null">depart=#{depart}</if>
WHERE tno=#{tno}
1.41.5
用于in子查询中的多个值的遍历:
DELETE FROM teachers WHERE tno IN
#{id}
调用代码:
Map<String,Object> map = new HashMap<String,Object>();
String[] ps = {“1”,“22”};
map.put(“ids”, ps );
mapper.delete(map);
1.42 MyBatis:接口映射方式
1.42.1 概述
在上面的测试用例中,在调用session的方法的时候,都会传入要调用的SQL的namespace+id名称,这不是必须的。可以只传入id即可。但是,如果在mybatis的环境中有多个相同id的映射名称,就会报错。所以,一般情况下,调用方法最好还是使用namespace+id。但是,namespace+id的使用方式很容易报错,因为是string类型的,没有检查。所以,mybatis提供了一种非常好的设计方式来避免这种问题,即Mapper接口。
1.42.2 Maven工程结构
注:导入jUnit5,使用@BeforeEach注解替代jUnit4的@Before。
1.42.3 创建Emp.java
package cn.tedu.pojo;
import java.util.Date;
public class Emp {
private int id;
private String ename;
private String job;
private double mgr;
private Date hiredate;
private double sal;
private double comm;
private int deptno;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public double getMgr() {
return mgr;
}
public void setMgr(double mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public double getComm() {
return comm;
}
public void setComm(double comm) {
this.comm = comm;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "Emp [id=" + id + ", ename=" + ename + ", job=" + job + ", mgr=" + mgr + ", hiredate=" + hiredate
+ ", sal=" + sal + ", comm=" + comm + ", deptno=" + deptno + "]";
}
}
1.42.4 创建EmpMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!-- SQL片段,提高SQL的复用 -->
<sql id="columns">
id,ename,job,mgr,hiredate,sal,comm,deptno
</sql>
<select id="getAll" resultType="cn.tedu.pojo.Emp">
<!-- select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp -->
<!-- 引用指定的SQL片段 -->
select <include refid="columns"></include> from emp
</select>
<!-- resultType必须配置 ,parameterType可以省略-->
<select id="getOne" resultType="cn.tedu.pojo.Emp" parameterType="int">
select <include refid="columns"></include> from emp
where
<!-- 判断条件,满足就执行SQL,不满足就不执行了 -->
<if test="x != 0" >id = #{x}</if>
</select>
<select id="getCount" resultType="int">
select count(*) from emp
</select>
<!-- Emp在核心配置文件里加了别名配置了 -->
<insert id="add" parameterType="Emp">
insert into emp( <include refid="columns"></include>)
values(null,
<if test="ename!=null">#{ename},</if>
<if test="job!=null">#{job},</if>
<if test="mgr!=0">#{mgr},</if>
NOW(),
<if test="sal!=0">#{sal},</if>
<if test="comm!=0">#{comm},</if>
<if test="deptno!=0">#{deptno}</if>
);
</insert>
<update id="update" parameterType="Emp">
update Emp
<set><!-- set能去多余逗号 -->
<if test="ename !=null">ename=#{ename},</if>
<if test="job !=null">job=#{job},</if>
<if test="mgr !=0">mgr=#{mgr},</if>
hiredate=NOW(),
<if test="sal !=0">sal=#{sal},</if>
<if test="comm !=0">comm=#{comm},</if>
<if test="deptno !=0"> deptno=#{deptno},</if>
</set>
<where>
<if test="id !=0">id=#{id}</if>
</where>
</update>
<delete id="del" parameterType="int">
delete from emp
<where>id=#{x}</where>
</delete>
<delete id="delmore">
delete from emp
<!-- where id in(1,2,3,4,5); -->
where id in
<!--获取map里的数据, ids是map里的key
<foreach collection="ids" open="(" close=")" separator="," item="i">
-->
<!--获取数组里的数据, array是固定写法
<foreach collection="array" open="(" close=")" separator="," item="i">
-->
<!--获取list里的数据, list是固定写法 -->
<foreach collection="list" open="(" close=")" separator="," item="i">
#{i}
</foreach>
</delete>
1.42.5 创建EmpMapper接口
注意:
1、 包名 + 类名 = UserMapper.xml中namespace的值
2、 接口中方法名 = mapper.xml中定义的id值
3、 方法的返回值类型和参数类型要和映射文件中一致
package cn.tedu.mapper;
import java.util.List;
import java.util.Map;
import cn.tedu.pojo.Emp;
public interface EmpMapper {
List<Emp> getAll();
Emp getOne(int id);
int getCount();
void add(Emp emp);
void update(Emp emp);
void del(int id);
void delmore(Map map);
void delmore(List list);
<T> void delmore(T[] t);
}
1.42.6 创建测试类
package cn.tedu.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import cn.tedu.mapper.EmpMapper;
import cn.tedu.pojo.Emp;
//接口开发模式
public class TestEmpInter {
private SqlSession session ;
@BeforeEach
public void init() throws IOException {
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
session = factory.openSession();
// session = factory.openSession(true);//开启自动提交
}
@Test
public void getAll() {
EmpMapper mapper = session.getMapper(EmpMapper.class);
List<Emp> list = mapper.getAll();
for (Emp e : list) {
System.out.println(e);
}
}
@Test
public void getOne() {
EmpMapper mapper=session.getMapper(EmpMapper.class);
Emp emp = mapper.getOne(100);
System.out.println(emp);
}
@Test
public void getCount() {
EmpMapper mapper = session.getMapper(EmpMapper.class);
int count = mapper.getCount();
System.out.println("总记录数:"+count);
}
@Test
public void add() {
EmpMapper mapper = session.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setEname("蔡徐坤2");
emp.setJob("rapper");
emp.setMgr(10);
emp.setSal(100);
emp.setComm(1000);
emp.setDeptno(100);
mapper.add(emp);
session.commit();//提交事务,commit()或者openSession(true)
}
@Test
public void update() {
EmpMapper mapper = session.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setId(502);
emp.setEname("王一博");
emp.setJob("topidol.");
emp.setMgr(1000);
emp.setSal(20000);
emp.setComm(99);
emp.setDeptno(88);
mapper.update(emp);
session.commit();
}
@Test
public void del() {
EmpMapper mapper = session.getMapper(EmpMapper.class);
mapper.del(503);
session.commit();
}
@Test
public void delmore() {
EmpMapper mapper = session.getMapper(EmpMapper.class);
// Map<String,Object> map = new HashMap<>();
// map.put(“ids”, new int[] {506,508,509});
// mapper.delmore(map);//用map封装数据
List list = new ArrayList();
Collections.addAll(list,500,400);
mapper.delmore(list);//用list封装数据
// mapper.delmore(new Integer[] {510,511});//用数组封装数据
session.commit();
}
}
1.43 ResultMap简单使用
1.43.1 概述
当数据库的字段名 和 对象的属性名 一致时,可以用简单属性resultType。
但是当 数据库中的字段名称 和 对象中的属性名称 不 一致时,就需要resultMap属性。
1.43.2 案例
<?xml version="1.0" encoding="UTF-8"?><!-- 最强大对象resultMap,结果封装到哪个pojo对象,type就是谁 -->
<resultMap type="Person" id="personRM">
<!-- 主键,property是对象的属性名,column是表里的字段名 -->
<id property="id" column="id"/>
<!-- 普通字段 -->
<result property="userName" column="user_name"/>
</resultMap>
<!-- 查询所有 -->
SELECT id,user_name FROM person WHERE id=#{id}
1.43.3 自动匹配规范驼峰规则
数据库中我们习惯使用全大写,多个单词用下划线隔开,而po对象中,习惯使用java驼峰规则。那一个一个手工写resultMap字段,浪费开发时间,直接配置一下就可以了。
如:
数据库字段: is_man
Javabean属性: private Integer isMan
mapper配置不需要写字段与属性的配置,会自动映射
注意:主键需要单独写,其它字段就可以直接利用驼峰规则自动映射。
第一步:在sqlMapConfig.xml中配置settings:
<?xml version="1.0" encoding="UTF-8" ?><settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
第二步:resultMap配置autoMapping=“true”
1.43.5.4 修改mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?><!-- 配置别名,在Mapper.xml文件里就可以直接用别名了,不用写那么全 -->
<typeAliases>
<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"/>
<typeAlias type="cn.tedu.pojo.Emp" alias="Emp"/>
<typeAlias type="cn.tedu.pojo.UserInfo" alias="UserInfo"/>
</typeAliases>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/DeptMapper.xml"/>
<mapper resource="mappers/EmpMapper.xml"/>
<mapper resource="mappers/UserInfoMapper.xml"/>
1.43.5.5 创建UserInfoMapper接口
package cn.tedu.mapper;
import java.util.List;
import cn.tedu.pojo.UserInfo;
public interface UserInfoMapper {
List<UserInfo> findAll();
}
1.43.5.6 创建TestUserInfo.java
package cn.tedu.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import cn.tedu.mapper.UserInfoMapper;
import cn.tedu.pojo.UserInfo;
public class TestUserInfo {
SqlSessionFactory factory ;
@BeforeEach
public void init() throws IOException {
InputStream in = Resources.getResourceAsStream("mysql-config.xml");
factory = new SqlSessionFactoryBuilder().build(in);
}
@Test
public void findAll() {
SqlSession session = factory.openSession(true);
UserInfoMapper mapper = session.getMapper(UserInfoMapper.class);
List<UserInfo> list = mapper.findAll();
for (UserInfo ui : list) {
System.out.println(ui);
}
}
}
1.44 ResultMap复杂使用
1.44.1 项目结构
1.44.2 pom.xml
官网添加mbatis和mysql依赖:
<?xml version="1.0" encoding="UTF-8"?><project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.44.3 准备数据库表
1.44.3.1 user_info表
create table user_info(
id int primary key auto_increment,
user_name varchar(100),
user_addr varchar(200),
user_age int);
insert into user_info values(null,‘韩梅梅’,‘上海’,20);
insert into user_info values(null,‘王海涛’,‘北京’,30);
insert into user_info values(null,‘张慎政’,‘河南’,10);
1.44.3.2 user_extra表
create table user_extra(
id int primary key auto_increment,
user_id int,
work varchar(100),
salary double);
insert into user_extra values(null,‘1’,‘程序员’,100000);
insert into user_extra values(null, ‘2’,‘教师’,1000);
insert into user_extra values(null, ‘3’,‘CTO’,100000);
1.44.3.3 orders表
create table orders(
id int primary key auto_increment,
user_id int,
order_no int,
order_desc varchar(100),
price double);
insert into orders values(null,1,100,‘好评’,1000);
insert into orders values(null,2,200,‘优秀’,100);
insert into orders values(null,1,300,‘优秀’,100);
insert into orders values(null,1,400,‘优秀’,100);
1.44.4 准备实体类
1.44.4.1 UserInfo.java
package cn.tedu.pojo;
//描述user_info表
public class UserInfo {
private int id;
private String userName;
private int userAge;
private String userAddr;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
public String getUserAddr() {
return userAddr;
}
public void setUserAddr(String userAddr) {
this.userAddr = userAddr;
}
@Override
public String toString() {
return "UserInfo [id=" + id + ", userName=" + userName + ", userAge=" + userAge + ", userAddr=" + userAddr
+ "]";
}
}
1.44.4.2 UserExtra.java
package cn.tedu.pojo;
//描述user_extra表
public class UserExtra {
private int id;
private int userId;
private String work;
private double salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getWork() {
return work;
}
public void setWork(String work) {
this.work = work;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "UserExtra [id=" + id + ", userId=" + userId + ", work=" + work + ", salary=" + salary + "]";
}
}
1.44.4.3 Orders.java
package cn.tedu.pojo;
//描述orders表
public class Orders {
private int id;
private int userId;
private int orderNo;
private double price;
private String orderDesc;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getOrderNo() {
return orderNo;
}
public void setOrderNo(int orderNo) {
this.orderNo = orderNo;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getOrderDesc() {
return orderDesc;
}
public void setOrderDesc(String orderDesc) {
this.orderDesc = orderDesc;
}
@Override
public String toString() {
return "Orders [id=" + id + ", userId=" + userId + ", orderNo=" + orderNo + ", price=" + price + ", orderDesc="
+ orderDesc + "]";
}
}
1.44.5 一对一
1.44.5.1 需求
user_extra 和 user_info表是一对一的关系
根据用户查询一对一关系的用户扩展信息,使用association + javaType描述关联表的信息
1.44.5.2 改造UserInfo类
package cn.tedu.pojo;
import java.util.List;
public class UserInfo {
private int id;
private String userName;
private String userAddr;
private int userAge;
//一对一
private UserExtra userExtra;
public UserExtra getUserExtra() {
return userExtra;
}
public void setUserExtra(UserExtra userExtra) {
this.userExtra = userExtra;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserAddr() {
return userAddr;
}
public void setUserAddr(String userAddr) {
this.userAddr = userAddr;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
@Override
public String toString() {
return "UserInfo [id=" + id + ", userName=" + userName + ", userAddr=" + userAddr + ", userAge=" + userAge
+ ", userExtra=" + userExtra + "]";
}
}
1.44.5.3 创建One21.xml
<?xml version="1.0" encoding="UTF-8"?><resultMap type="UserInfo" id="One21RM">
<!-- 描述主表 -->
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="user_age" property="userAge"/>
<result column="user_addr" property="userAddr"/>
<!-- 描述 一对一的 UserExtra association+javaType固定搭配 -->
<association property="userExtra" javaType="UserExtra">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="work" property="work"/>
<result column="salary" property="salary"/>
</association>
</resultMap>
<!-- 多表查询 -->
<select id="One21" resultMap="One21RM">
select * from user_info a,user_extra b
where a.id=b.user_id and a.id=#{id}
</select>
1.44.5.4 引入sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?><!-- 配置别名,在Mapper.xml文件里就可以直接用别名了,不用写那么全 -->
<typeAliases>
<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"/>
<typeAlias type="cn.tedu.pojo.Emp" alias="Emp"/>
<typeAlias type="cn.tedu.pojo.UserInfo" alias="UserInfo"/>
<typeAlias type="cn.tedu.pojo.UserExtra" alias="UserExtra"/>
</typeAliases>
<environments default="test">
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/DeptMapper.xml"/>
<mapper resource="mappers/EmpMapper.xml"/>
<mapper resource="mappers/UserInfoMapper.xml"/>
<mapper resource="mappers/One21.xml"/>
1.44.5.5 创建One21Mapper接口
package cn.tedu.mapper;
import cn.tedu.pojo.UserInfo;
public interface One21Mapper {
UserInfo One21(int userId);
}
1.44.5.6 测试类
package cn.tedu.test;
//测试 一对一
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import ch.qos.logback.core.net.ssl.SSL;
import cn.tedu.mapper.One21Mapper;
import cn.tedu.pojo.UserInfo;
public class TestOne21 {
private SqlSession session ;
@BeforeEach
public void init() throws IOException {
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
session = factory.openSession();
// session = factory.openSession(true);//开启自动提交
}
@Test
public void One21() {
One21Mapper mapper = session.getMapper(One21Mapper.class);
UserInfo info = mapper.One21(2);
//UserInfo [id=2, userName=王海涛, userAge=30, userAddr=北京, userExtra=UserExtra [id=2, userId=2, work=教师, salary=1000.0]]
System.out.println(info);
}
}
1.44.6 一对多
1.44.6.1 需求
user_info 和 orders表是一对多的关系
查询用户的所有订单,使用collection + ofType描述多的一方的信息
1.44.6.2 改造UserInfo对象
package cn.tedu.pojo;
import java.util.List;
public class UserInfo {
private int id;
private String userName;
private String userAddr;
private int userAge;
private UserExtra userExtra;
//一对多
private List<Orders> orders;
public List<Orders> getOrders() {
return orders;
}
public void setOrders(List<Orders> orders) {
this.orders = orders;
}
public UserExtra getUserExtra() {
return userExtra;
}
public void setUserExtra(UserExtra userExtra) {
this.userExtra = userExtra;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserAddr() {
return userAddr;
}
public void setUserAddr(String userAddr) {
this.userAddr = userAddr;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
@Override
public String toString() {
return "UserInfo [id=" + id + ", userName=" + userName + ", userAddr=" + userAddr + ", userAge=" + userAge
+ ", userExtra=" + userExtra + ", orders=" + orders + "]";
}
}
1.44.6.3 改造UserInfoMapper.xml
<?xml version="1.0" encoding="UTF-8"?><!-- 根据用户查询用户详情 -->
<resultMap type="cn.tedu.pojo.UserInfo" id="userRM">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="user_addr" property="userAddr"/>
<result column="user_age" property="userAge"/>
</resultMap>
<!-- 一对一 -->
<resultMap type="cn.tedu.pojo.UserInfo"
id="userExtraRM"
extends="userRM">
<association property="userExtra" javaType="cn.tedu.pojo.UserExtra">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="work" property="work"/>
<result column="salary" property="salary"/>
</association>
</resultMap>
<!-- 根据用户id查询用户详情 -->
<select id="findExtraByUser" resultMap="userExtraRM">
select * from
userinfo t1,userextra t2
where
t1.id=t2.user_id
and t1.id=#{id}
</select>
<resultMap type="cn.tedu.pojo.UserInfo"
id=“ordersRM”
extends=“userRM”>
<collection property="orders" ofType="cn.tedu.pojo.Orders">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="order_no" property="orderNo"/>
<result column="order_desc" property="orderDesc"/>
<result column="price" property="price"/>
</collection>
</resultMap>
1.44.6.4 增加关联查询的SQL
注意:结果集中,不要出现同名的字段,否则封装不成功!!如果出现重复字段,以结果集为准,重新映射column关系。
<select id="findOrdersByUser" resultMap="ordersRM">
select
t1.id,
t1.user_name,
t1.user_addr,
t1.user_age,
t2.id ,
t2.user_id,
t2.order_no,
t2.order_desc,
t2.price
from userinfo t1,orders t2
where t1.id=t2.user_id
and t1.id=#{id}
</select>
1.44.6.5 改造UserInfoMapper接口
package cn.tedu.dao;
import java.util.List;
import cn.tedu.pojo.Orders;
import cn.tedu.pojo.UserInfo;
public interface UserInfoDao {
//根据用户id查询用户详情,一对一
public UserInfo findExtraByUser(int userId);
//根据用户id查询用户的所有订单
public UserInfo findOrdersByUser(int userId);
}
1.44.6.6 改造测试类
package cn.tedu.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import cn.tedu.dao.UserInfoDao;
import cn.tedu.pojo.Orders;
import cn.tedu.pojo.UserInfo;
public class TestRelation {
SqlSessionFactory ssf ;
@Before
public void init(){
try {
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
ssf = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void association(){
SqlSession session = ssf.openSession();
UserInfoDao dao = session.getMapper(UserInfoDao.class);
UserInfo info = dao.findExtraByUser(1);
System.out.println(info);
session.close();
}
@Test
public void collection(){
SqlSession session = ssf.openSession();
UserInfoDao dao = session.getMapper(UserInfoDao.class);
UserInfo info = dao.findOrdersByUser(1);
System.out.println(info);
session.close();
}
}
1.44.6.7 问题:只查出来一条记录
一对多结果不对,只是取到一条记录。
注意:这是因为结果集中有重复字段出现!!
如:下面的SQL结果集中,u.id和o.id都叫id。
这时需要做两件事:
1、 在SQL中取别名加以区分
2、 在映射文件中,修改id的column值,要以查出来的结果集为准!!
1.45 扩展
1.45.1 JDBC和MyBatis的区别?
JDBC是java提供了一套专门用于和数据库对接的api,java.sql.*,其规范了如何和数据库进行对接,实现由各数据库厂商进行各自的实现和扩展。学习JDBC重点在学习如何使用其api。
MyBatis框架是轻量级封装了JDBC,我们已经看不到这些api,连接connection、语句preparedstatement、结果集ResultSet,而关注的是mybatis框架体系如何去使用,一旦写好,我们关注的是java对象。
1.45.2 XML和接口方式的区别?
MyBatis提供了两种操作数据库的方式,一种是通过xml映射文件,一种是通过java的接口类。按面向对象方式更加推荐接口方式,但如果复杂的多表映射,仍然需要使用xml映射文件的ResultMap方式实现。
接口只是假象,其底层仍然是通过xml实现,好不容易实现了一套方式,怎忍丢掉呢?可以做个测试就知道它底层怎么实现的?把xml中的sql删除,它就玩不转了。
1.45.3 接口方式怎么找到xml执行的?
SqlSession的getMapper方法找到类,通过反射可以获取到类的全路径(包名.类名),相加后就定位到某个xml的命名空间namespace,在根据调用的方法去找到xml中某个标签的id属性。从而实现价值接口,调用接口的方法而间接找到xml中的标签,通过解析xml获取这个标签的内容,从而获取到sql语句。