黑马程序员SSM学习记录(第7-16章)

本文详细记录了黑马程序员SSM(Spring、SpringMVC、MyBatis)框架的学习过程,涵盖Spring的IoC和DI原理,以及SpringMVC的拦截器、异常处理和事务管理。同时,讲解了MyBatis的 Dao 层实现,包括映射文件的深入配置和多表操作。此外,还介绍了SpringMVC的注解开发、单元测试和Web环境的集成。文章深入探讨了声明式事务控制,包括XML和注解两种方式,并展示了MyBatis的注解开发,简化了Mapper的配置。最后,文章提到了SSM框架的整合,为后续的学习和实践提供了基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Maven

黑马SSM课程笔记:https://blog.youkuaiyun.com/weixin_43973189/article/details/121984883
Spring
第一章 Spring的IoC和DI

Spring程序开发步骤

1、导入Spring开发的基本包坐标

2、编写Dao接口和实现类

3、创建Spring核心配置文件
在Spring配置文件中配置UserDaolmpl

4、使用Spring的API获得Bean实例

#Spring配置文件
1、Bean标签基本配置

2、Bean标签范围配置

3、Bean生命周期配置(不是很重要)
● init-method:指定类中的初始化方法名称
● destroy-method:指定类中销毁方法名称

4、Bean实例化三种方式
(1)使用无参构造方法实例化(掌握这种)
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败

(2)工厂静态方法实例化
工厂的静态方法返回Bean实例

(3)工厂实例方法实例化
工厂的非静态方法返回Bean实例

5、Bean的依赖注入入门
① 创建 UserService,UserService 内部调用 UserDao的save() 方法

② 将 UserServiceImpl 的创建权交给 Spring

③ 从 Spring 容器中获得 UserService 进行操作

6、Bean的依赖注入分析

7、Bean的依赖注入概念
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
怎么将UserDao怎样注入到UserService内部呢?
● 构造方法
● set方法
(1)set方法注入
在UserServiceImpl中添加setUserDao方法

配置Spring容器调用set方法进行注入

P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入P命名空间:

其次,需要修改注入方式

(2)构造方法注入
创建有参构造

8、Bean的依赖注入的数据类型
上面的操作,都是注入的引用Bean,处了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。

注入数据的三种数据类型
● 普通数据类型
● 引用数据类型
● 集合数据类型

其中引用数据类型,此处就不再赘述了,之前的操作都是对UserDao对象的引用进行注入的,下面将以set方法注入为例,演示普通数据类型和集合数据类型的注入。
(1)普通数据类型的注入

(2)集合数据类型(List)的注入

(3)集合数据类型(List)的注入

(4)集合数据类型( Map<String,User> )的注入

(5)集合数据类型(Properties)的注入

9、引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载

10、知识要点
Spring的重点配置

#Spring相关API
1、ApplicationContext的继承体系
applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象

2、ApplicationContext的实现类
(1)ClassPathXmlApplicationContext
它是从类的根路径下(resources文件夹下)加载配置文件 推荐使用这种
例如:

(2)FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
例如:

(3)AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
3、getBean()方法使用

其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。
当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。

4、知识要点
Spring的重点API

第二章 IoC和DI注解开发
1.Spring配置数据源

1.1 数据源(连接池)的作用
● 数据源(连接池)是提高程序性能如出现的
● 事先实例化数据源,初始化部分连接资源
● 使用连接资源时从数据源中获取
● 使用完毕后将连接资源归还给数据源

常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等

数据源的开发步骤:
① 导入数据源的坐标和数据库驱动坐标
② 创建数据源对象
③ 设置数据源的基本连接数据
④ 使用数据源获取连接资源和归还连接资源
1.2 数据源的手动创建
① 导入c3p0和druid的坐标

导入mysql数据库驱动坐标

② 创建C3P0连接池

② 创建Druid连接池

③ 提取jdbc.properties配置文件

④ 读取jdbc.properties配置文件创建连接池

1.3 Spring配置数据源
可以将DataSource的创建权交由Spring容器去完成
● DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的
● DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入

测试从容器当中获取数据源

1.4 抽取jdbc配置文件
applicationContext.xml加载jdbc.properties配置文件获得连接信息。
首先,需要引入context命名空间和约束路径:
● 命名空间:xmlns:context=“http://www.springframework.org/schema/context”
● 约束路径:http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
jdbc.properties文件内容:

1.5 知识要点
Spring容器加载properties文件

  1. Spring注解开发
    2.1 Spring原始注解
    Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。

常见注解:
一: 组件类注解
Spring使用配置文件或者注解的方式标识需要处理的java类,把对应Java类当成bean注册到容器。
@Component :标准一个普通的spring Bean类。 @Repository:标注一个DAO组件类。 @Service:标注一个业务逻辑组件类。 @Controller:标注一个控制器组件类。 这些都是注解在平时的开发过程中出镜率极高,@Component、@Repository、@Service、@Controller实质上属于同一类注解,用法相同,功能相同,区别在于标识组件的类型。 @Component可以代替@Repository、@Service、@Controller,因为这三个注解是被@Component标注的。
注意:
1、被注解的java类当做Bean实例,Bean实例的名称默认是Bean类的首字母小写,其他部分不变。@Service也可以自定义Bean名称,但是必须是唯一的!
2、尽量使用对应组件注解的类替换@Component注解,便于开发和维护。
3、指定了某些类可作为Spring Bean类使用后,最好还需要让spring搜索指定路径,在Spring配置文件加入如下配置:

<!-- 自动扫描指定包及其子包下的所有Bean-->
<context:component-scan base-package="org.springframework.*"/>

二:装配bean时常用的注解
1、注解介绍
@Autowired:属于Spring 的org.springframework.beans.factory.annotation包下,可用于为类的属性、构造器、方法进行注值 @Resource:不属于spring的注解,而是来自于JSR-250位于java.annotation包下,使用该annotation为目标bean指定协作者Bean。 @PostConstruct 和 @PreDestroy 方法 实现初始化和销毁bean之前进行的操作
注意:
1、@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。
2、@Autowired只按照Type 注入;@Resource默认按Name自动注入,也提供按照Type 注入
3、@Resource注解的使用性更为灵活,可指定名称,也可以指定类型 ;@Autowired注解进行装配容易抛出异常,特别是装配的bean类型有多个的时候,而解决的办法是需要在增加@Qualifier进行限定

三:@Component vs @Configuration and @Bean
@Component可以替代 @Configuration注解

四:spring MVC模块注解
见3.3 SpringMVC注解解析

五:Spring事务模块注解
1、常用到的注解
在处理dao层或service层的事务操作时,譬如删除失败时的回滚操作。使用**@Transactional** 作为注解,需要在配置文件激活

<!-- 开启注解方式声明事务 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

2、举例

@Service
public class CompanyServiceImpl implements CompanyService {
  @Autowired
  private CompanyDAO companyDAO;

  @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
  public int deleteByName(String name) {

    int result = companyDAO.deleteByName(name);
    return result;
  }
  ...
}

readOnly : 事务的读写属性,取true或者false,true为只读、默认为false
rollbackFor : 回滚策略,当遇到指定异常时回滚。譬如上例遇到异常就回滚
timeout (补充的) : 设置超时时间,单位为秒
isolation : 设置事务隔离级别,枚举类型,一共五种

3、总结
事务的传播机制和隔离机制
在这里插入图片描述
在这里插入图片描述

Spring原始注解主要是替代的配置

注意:
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

使用@Compont或@Repository标识UserDaoImpl需要Spring进行实例化。

● 使用@Compont或@Service标识UserServiceImpl需要Spring进行实例化
● 使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入

● 使用@Value进行字符串的注入

● 使用@Scope标注Bean的范围

● 使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法

2.2 Spring新注解
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
● 非自定义的Bean的配置:
● 加载properties文件的配置:context:property-placeholder
● 组件扫描的配置:context:component-scan
● 引入其他文件:

● @Configuration
● @ComponentScan
● @Import

● @PropertySource
● @Value

● @Bean

测试加载核心配置类创建Spring容器

  1. Spring集成Junit
    3.1在测试类中,每个测试方法都有以下两行代码:

这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
3.2上述问题解决思路
● 让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它
● 将需要进行测试Bean直接在测试类中进行注入
3.3 Spring集成Junit步骤
① 导入spring集成Junit的坐标
② 使用@Runwith注解替换原来的运行期
③ 使用@ContextConfiguration指定配置文件或配置类
④ 使用@Autowired注入需要测试的对象
⑤ 创建测试方法进行测试

3.4 Spring集成Junit代码实现
① 导入spring集成Junit的坐标

② 使用@Runwith注解替换原来的运行期

③ 使用@ContextConfiguration指定配置文件或配置类

④ 使用@Autowired注入需要测试的对象

⑤ 创建测试方法进行测试

3.5 知识要点
Spring集成Junit步骤
① 导入spring集成Junit的坐标
② 使用@Runwith注解替换原来的运行期
③ 使用@ContextConfiguration指定配置文件或配置类
④ 使用@Autowired注入需要测试的对象
⑤ 创建测试方法进行测试

第三章 SpringMVC入门

  1. Spring集成web环境
    1.1 ApplicationContext应用上下文获取方式

应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但是每次从容器中获得Bean时都要编写new ClasspathXmlApplicationContext(spring配置文件) ,这样的弊端是配置文件加载多次,应用上下文对象创建多次。

在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

1.2 Spring提供获取应用上下文的工具

上面的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。

所以我们需要做的只有两件事:
① 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
② 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext

1.3 导入Spring集成web的坐标

1.4 配置ContextLoaderListener监听器

1.5 通过工具获得应用上下文对象

1.6 知识要点
Spring集成web环境步骤
① 配置ContextLoaderListener监听器
② 使用WebApplicationContextUtils获得应用上下文

  1. SpringMVC 简介
    2.1 SpringMVC雏形

2.2 SpringMVC快速入门
需求:客户端发起请求,服务器端接收请求,执行逻辑并进行视图跳转。
开发步骤:

① 导入SpringMVC相关坐标
② 配置SpringMVC核心控制器DispathcerServlet
③ 创建Controller类和视图页面
④ 使用注解配置Controller类中业务方法的映射地址
⑤ 配置SpringMVC核心文件 spring-mvc.xml
⑥ 客户端发起请求测试

① 导入Spring和SpringMVC的坐标

① 导入Servlet和Jsp的坐标

② 在web.xml配置SpringMVC的核心控制器

③ 创建Controller和业务方法

③ 创建视图页面success.jsp

④ 配置注解

⑤ 创建spring-mvc.xml

⑥ 访问测试地址

Spring访问流程(代码角度)

2.3 SpringMVC流程图示

2.4 知识要点
SpringMVC的开发步骤
① 导入SpringMVC相关坐标
② 配置SpringMVC核心控制器DispathcerServlet
③ 创建Controller类和视图页面
④ 使用注解配置Controller类中业务方法的映射地址
⑤ 配置SpringMVC核心文件 spring-mvc.xml
⑥ 客户端发起请求测试

SpringMVC 组件解析
3.1 SpringMVC的执行流程

① 用户发送请求至前端控制器DispatcherServlet。
② DispatcherServlet收到请求调用HandlerMapping处理器映射器。
③ 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
④ DispatcherServlet调用HandlerAdapter处理器适配器。
⑤ HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
⑥ Controller执行完成返回ModelAndView。
⑦ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
⑧ DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
⑨ ViewReslover解析后返回具体View。
⑩ DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。

3.2 SpringMVC组件解析

前端控制器:DispatcherServlet
用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。

处理器映射器:HandlerMapping
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

处理器适配器:HandlerAdapter
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

处理器:Handler
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。

视图解析器:View Resolver
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

视图:View
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
3.3 SpringMVC注解解析
常用注解
@Controller
作用:@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。这个时候就需要我们把这个控制器类交给Spring 来管理。有两种方式可以管理:

<!--方式一-->
<bean class="com.cqvie.handler.HelloWorld"/>
<!--方式二-->
< context:component-scan base-package = "com.cqvie" /> <!-- 路径写到controller的上一层 -->

@RequestMapping
作用:用于建立请求 URL 和处理请求方法之间的对应关系。RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。返回值会通过视图解析器解析为实际的物理视图,对于 InternalResourceViewResolver 视图解析器,会做如下的解析:通过 prefix + returnVal + suffix 这样的方式得到实际的物理视图,然后做转发操作。

<!-- 配置视图解析器:如何把 handler 方法返回值解析为实际的物理视图 -->
     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
         <property name="prefix" value="/WEB-INF/views/"></property>
         <property name="suffix" value=".jsp"></property>
     </bean>

RequestMapping注解有六个属性:

1、 value

value:指定请求的实际地址;

2、method;

method: 指定请求的method类型, GET、POST、PUT、DELETE等
3、consumes

consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;

4、produces

produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

5、params

params: 指定request中必须包含某些参数值是,才让该方法处理。

6、headers

headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。

@ResponseBody
告知SpringMVC框架,方法返回的字符串不是跳转,而是直接在http响应体中返回。(方法返回的东西会通过IO流的方式写入到浏览器。)

位置:
● 类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
● 方法上,请求 URL 的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径

属性:
● value:用于指定请求的URL。它和path属性的作用是一样的
● method:用于指定请求的方式
● params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样
例如:
● params = {“accountName”},表示请求参数必须有accountName
● params = {“moeny!100”},表示请求参数中money不能是100

@Resource和@Autowired
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1、共同点
    两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
   
2、不同点
(1)@Autowired
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

public class HelloWorld{
    // 下面两种@Autowired只要使用一种即可
    @Autowired
    private UserDao userDao; // 用于字段上
    
    @Autowired
    public void setUserDao(UserDao userDao) { // 用于属性的方法上
        this.userDao = userDao;
    }
}

@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

public class HelloWorld{ 
  @Autowired 
  @Qualifier("userDao") 
  private UserDao userDao; 
}

(2)@Resource
@Resource默认按照ByName自动注入,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

public class HelloWorld{
    // 下面两种@Resource只要使用一种即可
    @Resource(name="userDao")
    private UserDao userDao; // 用于字段上
    
    @Resource(name="userDao")
    public void setUserDao(UserDao userDao) { // 用于属性的setter方法上
        this.userDao = userDao;
    }
}

@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。如:

@Controller  
public class TestController {  
     @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)  
     public String getLogin(@PathVariable("userId") String userId,  
         @PathVariable("roleId") String roleId){  
         System.out.println("User Id : " + userId);  
         System.out.println("Role Id : " + roleId);  
         return "hello";  
     }  
     @RequestMapping(value="/product/{productId}",method = RequestMethod.GET)  
     public String getProduct(@PathVariable("productId") String productId){  
           System.out.println("Product Id : " + productId);  
           return "hello";  
     }  
     @RequestMapping(value="/javabeat/{regexp1:[a-z-]+}",  
           method = RequestMethod.GET)  
     public String getRegExp(@PathVariable("regexp1") String regexp1){  
           System.out.println("URI Part 1 : " + regexp1);  
           return "hello";  
     }  
}  

@CookieValue
  作用:用来获取Cookie中的值;
  参数: value:参数名称 required:是否必须 defaultValue:默认值
  使用案例:

@RequestMapping("/testCookieValue")
    public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
        System.out.println("JSESSIONID = " + sessionId);
        return "success";
    }

@RequestParam
当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。

@RequestMapping(value = "/quick16")
@ResponseBody
public void save16(@RequestParam("name") String username) {
    System.out.println(username);
}

注解@RequestParam还有如下参数可以使用:
● value:与请求参数名称
● required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
● defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
@SessionAttributes
  @SessionAttributes即将值放到session作用域中,写在class上面。  
  @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外(value 属性值),
还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(types 属性值),用例:

@SessionAttributes(value = {"user"}, types = {String.class})
@RequestMapping("/springmvc")
@Controller
public class SessionAttributesTest {
    
    /**
     * @SessionAttributes
     *         除了可以通过属性名指定需要放到会话中的属性外(value 属性值),
     *         还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(types 属性值)。
     * 注意: 该注解只能放在类的上面,不能放在方法上面
     * 
     * @return
     */
    @RequestMapping("/testSessionAttributes")
    public String testSessionAttributes(Map<String, Object> map) {
        User user = new User(1, "刘邦", "qwe", "123", "辽宁");
        map.put("user", user);
        map.put("school", "重庆");
        return "success";
    }
}

@ResponseBody 
  作用: 告知SpringMVC框架,方法回的字符串不是跳转是直接在http响应体中返回。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;

3.2组件扫描
SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用<context:component-scan base-package=“com.itheima.controller"/>进行组件扫描。
3.4 SpringMVC的XML配置解析

视图解析器
SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:

翻看该解析器源码,可以看到该解析器的默认设置,如下:

可以通过属性注入的方式修改视图的的前后缀

3.5 知识要点
SpringMVC的相关组件
• 前端控制器:DispatcherServlet
• 处理器映射器:HandlerMapping
• 处理器适配器:HandlerAdapter
• 处理器:Handler
• 视图解析器:View Resolver
• 视图:View
SpringMVC的注解和配置
• 请求映射注解:@RequestMapping
• 视图解析器配置:

第四章 SpringMVC的请求和响应
SpringMVC的数据响应
1.1 SpringMVC的数据响应方式

1) 页面跳转
● 直接返回字符串
● 通过ModelAndView对象返回

2) 回写数据
● 直接返回字符串
● 返回对象或集合

1.2 页面跳转
(1)返回字符串形式
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。

返回带有前缀的字符串:
转发:forward:/WEB-INF/views/index.jsp
重定向:redirect:/index.jsp(因为webinfo文件夹[即WEB-INF]是受保护的,所以要把重定向的jsp放到可以直接访问的地方)
(2)返回ModelAndView对象

(3)向request域存储数据
在进行转发时,往往要向request域中存储数据,在jsp页面中显示,那么Controller中怎样向request域中存储数据呢?

① 通过SpringMVC框架注入的request对象setAttribute()方法设置

② 通过ModelAndView的addObject()方法设置

1.3回写数据
(1)直接返回字符串
Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print(“hello world”) 即可,那么在Controller中想直接回写字符串该怎样呢?

① 通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”) 回写数据,此时不需要视图跳转,业务方法返回值为void。

② 将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法
返回的字符串不是跳转是直接在http响应体中返回。

在异步项目中,客户端与服务器端往往要进行json格式字符串交互,此时我们可以手动拼接json字符串返回。

手动拼接json格式字符串的方式很麻烦,开发中往往要将复杂的java对象转换成json格式的字符串,我们可以使用web阶段学习过的json转换工具jackson进行转换,导入jackson坐标。

通过jackson转换json格式字符串,回写字符串。

(2)返回对象或集合
通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:

在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,可以使用mvc的注解驱动代替上述配置。

在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使用mvc:annotation-driven自动加载 RequestMappingHandlerMapping(处理映射器)和
RequestMappingHandlerAdapter( 处 理 适 配 器 ),可用在Spring-xml.xml配置文件中使用mvc:annotation-driven替代注解处理器和适配器的配置。
同时使用mvc:annotation-driven默认底层就会集成jackson进行对象或集合的json格式字符串的转换。
1.4 知识要点
SpringMVC的数据响应方式
1) 页面跳转
● 直接返回字符串
● 通过ModelAndView对象返回

2) 回写数据
● 直接返回字符串
● 返回对象或集合

  1. SpringMVC 获得请求数据
    2.1 获得请求参数
    客户端请求参数的格式是:name=value&name=value… …
    服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:
    ● 基本类型参数
    ● POJO类型参数(简单的bean)
    ● 数组类型参数
    ● 集合类型参数
    2.2 获得基本类型参数
    Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。

2.3 获得POJO类型参数
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。

2.4 获得数组类型参数
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。

2.5 获得集合类型参数
获得集合参数时,要将集合参数包装到一个POJO中才可以。

当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。

注意:通过谷歌开发者工具抓包发现,没有加载到jquery文件,原因是SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是/,代表对所有的资源都进行过滤操作,我们可以通过以下两种
方式指定放行静态资源:
● 在spring-mvc.xml配置文件中指定放行的资源

● 直接使用mvc:default-servlet-handler/标签
2.6 请求数据乱码问题
当post请求时,数据会出现乱码,可以设置一个过滤器来进行编码的过滤。

2.7 参数绑定注解@requestParam
当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。

注解@RequestParam还有如下参数可以使用:
● value:与请求参数名称
● required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
● defaultValue:当没有指定请求参数时,则使用指定的默认值赋值

2.8 获得Restful风格的参数
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。
Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下:
● GET:用于获取资源
● POST:用于新建资源
● PUT:用于更新资源
● DELETE:用于删除资源

例如:
● /user/1 GET : 得到 id = 1 的 user
● /user/1 DELETE: 删除 id = 1 的 user
● /user/1 PUT: 更新 id = 1 的 user
● /user POST: 新增 user
上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作。

2.9 自定义类型转换器
● SpringMVC 默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。
● 但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。

自定义类型转换器的开发步骤:
① 定义转换器类实现Converter接口
② 在配置文件中声明转换器
③ 在中引用转换器

① 定义转换器类实现Converter接口

② 在配置文件中声明转换器

③ 在中引用转换器

2.10 获得Servlet相关API
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
● HttpServletRequest
● HttpServletResponse
● HttpSession

输出:
org.apache.catalina.connector.RequestFacade@143935e
org.apache.catalina.connector.ResponseFacade@14ac0c3
org.apache.catalina.session.StandardSessionFacade@1daa35e
这里输出的是catalina,等价于这里生成的三个对象是tomcat原生产生的,然后传给springmvc框架,调用方法时引用传递给参数。
2.11 获得请求头
(1)@RequestHeader
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestHeader注解的属性如下:
● value:请求头的名称
● required:是否必须携带此请求头

(2)@CookieValue
使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:
● value:指定cookie的名称
● required:是否必须携带此cookie

2.12 文件上传
(1)文件上传客户端三要素
● 表单项type=“file”
● 表单的提交方式是post
● 表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”

(2)文件上传原理
● 当form表单修改为多部分表单时,request.getParameter()将失效。
● enctype=“application/x-www-form-urlencoded”时,form表单的正文内容格式是:
key=value&key=value&key=value
● 当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成多部分形式:

2.13 单文件上传步骤
① 导入fileupload和io坐标
② 配置文件上传解析器
③ 编写文件上传代码

2.14 单文件上传实现
① 导入fileupload和io坐标

commons-fileupload
commons-fileupload
1.3.1


commons-io
commons-io
2.3

② 配置文件上传解析器

③ 编写文件上传代码

@RequestMapping(value = “/quick22”)
@ResponseBody
public void save22(String username, MultipartFile uploadFile) throws IOException {
System.out.println(username);
//获得上传文件的名称
String originalFilename = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File(“E:\图片\” + originalFilename));
}
2.15 多文件上传实现
多文件上传,只需要将页面修改为多个文件上传项,将方法参数MultipartFile类型修改为MultipartFile[]即可

名称:
文件1:
文件2:
文件3:

@RequestMapping(value = “/quick23”)
@ResponseBody
public void save23(String username, MultipartFile[] uploadFile) throws IOException {
System.out.println(username);
for (MultipartFile multipartFile : uploadFile) {
String originalFilename = multipartFile.getOriginalFilename();
multipartFile.transferTo(new File(“E:\图片\” + originalFilename));
}
}
2.16 知识要点
MVC实现数据请求方式
● 基本类型参数
● POJO类型参数
● 数组类型参数
● 集合类型参数

MVC获取数据细节
● 中文乱码问题
● @RequestParam 和 @PathVariable
● 自定义类型转换器
● 获得Servlet相关API
● @RequestHeader 和 @CookieValue
● 文件上传

第五章 JdbcTemplate
知识要点
① 导入spring-jdbc和spring-tx坐标
② 创建数据库表和实体
③ 创建JdbcTemplate对象
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);

④ 执行数据库操作
更新操作:
jdbcTemplate.update (sql,params)
查询操作:
jdbcTemplate.query (sql,Mapper,params)
jdbcTemplate.queryForObject(sql,Mapper,params)

第六章 Spring练习(itheima_spring_test工程)
applicationContext.xml:spring核心配置文件,配置bean(如业务层、Dao层的bean)
spring-mvc.xml:SpringMVC的核心配置文件,配置注解驱动、内部资源视图解析器、静态资源访问
jdbc.properties:抽取JDBC数据库连接信息
log4j.properties:关于日志的配置文件(不需要会写)

第七章 SpringMVC拦截器

1、SpringMVC拦截器
1.1 拦截器(interceptor)的作用
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。

将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。

1.2 拦截器和过滤器区别
在这里插入图片描述
1.3 拦截器的快速入门
自定义拦截器很简单,只有如下三步:
① 创建拦截器类实现HandlerInterceptor接口
② 配置拦截器
③ 测试拦截器的拦截效果

① 创建拦截器类实现HandlerInterceptor接口

public class Intercepter1 implements HandlerInterceptor {
    //目标方法执行之前 执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return false;
    }
    //在目标方法执行之后 视图对象返回之前
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("post");
    }
    //在流程都执行完毕之后
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("after");
    }
}

② 配置拦截器

<!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**/"/>
            <bean class="com.itheima.interceptor.Intercepter1"/>
        </mvc:interceptor>
    </mvc:interceptors>

③ 测试拦截器的拦截效果(编写目标方法)

@Controller
public class TargetController {

    @RequestMapping("/target")
    public ModelAndView show(){
        System.out.println("目标资源执行......");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("name","itcast");
        modelAndView.setViewName("index");
        return modelAndView;
    }

}

访问:http://localhost:8080/target
控制台打印结果
在这里插入图片描述
1.4多拦截器操作
同上,在编写一个MyHandlerInterceptor2操作,测试执行顺序
在这里插入图片描述
1.5 拦截器方法说明
在这里插入图片描述
1.6 知识要点
自定义拦截器步骤
① 创建拦截器类实现HandlerInterceptor接口
② 配置拦截器
③ 测试拦截器的拦截效果

第八章 SpringMVC异常处理机制

SpringMVC异常处理
1.1 异常处理的思路
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。

系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,如下图:

在这里插入图片描述
1.2 异常处理两种方式
● 使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver
● 实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器

1.3 简单异常处理器SimpleMappingExceptionResolver
SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置(spring-mvc.xml中)

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!--<property name="defaultErrorView" value="error"/>-->
    <property name="exceptionMappings">
        <map>
        	<!--key:出现的异常类型,value:跳转到的界面-->
            <entry key="java.lang.ClassCastException" value="error1"/>
            <entry key="com.itheima.exception.MyException" value="error2"/>
        </map>
    </property>
</bean>

1.4 自定义异常处理步骤
① 创建异常处理器类实现HandlerExceptionResolver
② 配置异常处理器
③ 编写异常页面
④ 测试异常跳转

① 创建异常处理器类实现HandlerExceptionResolver

public class MyExceptionResolver implements HandlerExceptionResolver {
    /*
       参数Exception:异常对象
       返回值ModelAndView:跳转到错误视图信息
    */
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        if (e instanceof MyException) {
            modelAndView.addObject("info", "自定义异常");
        } else if (e instanceof ClassCastException) {
            modelAndView.addObject("info", "类转换异常");
        }
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

② 配置异常处理器

<!--自定义异常处理器-->
<bean class="com.itheima.resolver.MyExceptionResolver"></bean>

③ 编写异常页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>通用的错误提示页面</h1>
    <h1>${info}</h1>
</body>
</html>

④ 测试异常跳转

1.5 知识要点
异常处理方式
● 配置简单异常处理器SimpleMappingExceptionResolver
● 自定义异常处理器

自定义异常处理步骤
① 创建异常处理器类实现HandlerExceptionResolver
② 配置异常处理器
③ 编写异常页面
④ 测试异常跳转
第九章 面向切面编程AOP
Spring 的 AOP 简介
1.1 什么是 AOP
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2 AOP 的作用及其优势
● 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
● 优势:减少重复代码,提高开发效率,并且便于维护

1.3 AOP 的底层实现
实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
1.4 AOP 的动态代理技术
常用的动态代理技术
● JDK 代理 : 基于接口的动态代理技术
● cglib 代理:基于父类的动态代理技术

在这里插入图片描述
1.5 JDK 的动态代理
① 目标类接口

public interface TargetInterface {
    public void save();
}

② 目标类

public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}

③ 增强代码

public class Advice {
    public void before() {
        System.out.println("前置增强....");
    }
    public void afterReturning() {
        System.out.println("后置增强....");
    }
}

④ 动态代理代码

//目标对象
final Target target = new Target();

//增强对象
final Advice advice = new Advice();

//返回值 就是动态生成的代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
        target.getClass().getClassLoader(), //目标对象类加载器
        target.getClass().getInterfaces(), //目标对象相同的接口字节码对象数组
        new InvocationHandler() {
            //调用代理对象的任何方法  实质执行的都是invoke方法
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                advice.before(); //前置增强
                Object invoke = method.invoke(target, args);//执行目标方法
                advice.afterReturning(); //后置增强
                return invoke;
            }
        }
);

⑤ 调用代理对象的方法测试

//调用代理对象的方法
proxy.save();
1
2
输出结果:

前置增强....
save running.....
后置增强....

1.6 cglib 的动态代理
① 目标类

public class Target {
    public void save() {
        System.out.println("save running.....");
    }
}

② 动态代理代码

//目标对象
final Target target = new Target();
//增强对象
final Advice advice = new Advice();
//返回值 就是动态生成的代理对象  基于cglib
//1、创建增强器
Enhancer enhancer = new Enhancer();
//2、设置父类(目标)
enhancer.setSuperclass(Target.class);
//3、设置回调
enhancer.setCallback(new MethodInterceptor() {
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        advice.before(); //执行前置
        Object invoke = method.invoke(target, args);//执行目标
        advice.afterReturning(); //执行后置
        return invoke;
    }
});
//4、创建代理对象
Target proxy = (Target) enhancer.create();

③ 调用代理对象的方法测试

proxy.save();
前置增强....
save running.....
后置增强....

1.7 AOP 相关概念
Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:
● Target(目标对象):代理的目标对象
● Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
● Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。通俗的说,可以被增强的方法叫连接点
● Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
● Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
● Aspect(切面):是切入点和通知(引介)的结合
● Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
1.8 AOP 开发明确的事项
(1)需要编写的内容
● 编写核心业务代码(目标类的目标方法)
● 编写切面类,切面类中有通知(增强功能方法)
● 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

(2)AOP 技术实现的内容
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

(3)AOP 底层使用哪种代理方式
在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

1.9 知识要点
● aop:面向切面编程
● aop底层实现:基于JDK的动态代理 和 基于Cglib的动态代理
● aop的重点概念:
Pointcut(切入点):被增强的方法
Advice(通知/ 增强):封装增强业务逻辑的方法
Aspect(切面):切点+通知
Weaving(织入):将切点与通知结合的过程

● 开发明确事项:
谁是切点(切点表达式配置)
谁是通知(切面类中的增强方法)
将切点和通知进行织入配置

2.基于 XML 的 AOP 开发
2.1 快速入门
① 导入 AOP 相关坐标
② 创建目标接口和目标类(内部有切点)
③ 创建切面类(内部有增强方法)
④ 将目标类和切面类的对象创建权交给 spring
⑤ 在 applicationContext.xml 中配置织入关系
⑥ 测试代码

① 导入 AOP 相关坐标

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.4</version>
</dependency>

② 创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void save();
}
public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}

③ 创建切面类(内部有增强方法)

public class MyAspect {
    public void before(){
        System.out.println("前置增强..........");
    }
}

④ 将目标类和切面类的对象创建权交给 spring

<bean id="target" class="com.itheima.dao.impl.target"></bean>
<bean id="myAspect" class="com.itheima.dao.impl.MyAspect"></bean>

⑤ 在 applicationContext.xml 中配置织入关系

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">

⑥ 测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.save();
    }
}

⑦ 测试结果

前置增强..........
save running.....

2.2 XML 配置 AOP 详解
(1)切点表达式的写法
表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

● 访问修饰符可以省略
● 返回值类型、包名、类名、方法名可以使用星号* 代表任意
● 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
● 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
例如:

execution(public void com.itheima.aop.Target.method())
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))

(2)通知的类型
通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

在这里插入图片描述
(3)切点表达式的抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。

<aop:config>
	<!--引用myAspect的Bean为切面对象-->
	<aop:aspect ref="myAspect">
		<aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
		<aop:before method="before" pointcut-ref="myPointcut"></aop:before>
	</aop:aspect>
</aop:config>

2.3 知识要点
● aop织入的配置

<aop:config>
	<aop:aspect ref=“切面类”>
		<aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>
	</aop:aspect>
</aop:config>

● 通知的类型:前置通知、后置通知、环绕通知、异常抛出通知、最终通知
● 切点表达式的写法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

3.1 快速入门
基于注解的aop开发步骤:
① 创建目标接口和目标类(内部有切点)
② 创建切面类(内部有增强方法)
③ 将目标类和切面类的对象创建权交给 spring
④ 在切面类中使用注解配置织入关系
⑤ 在配置文件中开启组件扫描和 AOP 的自动代理
⑥ 测试

① 创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void save();
}

public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}

② 创建切面类(内部有增强方法)

public class MyAspect {
	//前置增强方法
    public void before(){
        System.out.println("前置增强..........");
    }
}

③ 将目标类和切面类的对象创建权交给 spring

@Component("target")
public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
        //int i = 1/0;
    }
}

@Component("myAspect")
public class MyAspect {
    public void before(){
        System.out.println("前置增强..........");
    }
}

④ 在切面类中使用注解配置织入关系

@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {
    //配置前置通知
    @Before("execution(* com.itheima.anno.*.*(..))")
    public void before(){
        System.out.println("前置增强..........");
    }
}

⑤ 在applicationContext-anno.xml配置文件中开启组件扫描和 AOP 的自动代理

<!--组件扫描-->
<context:component-scan base-package="com.itheima.anno"></context:component-scan>

<!--aop自动代理(加了这句话才会去识别aop相关的注解)-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

⑥ 测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoTest {
    @Autowired
    private TargetInterface target;

    @Test
    public void test1(){
        target.save();
    }
}

⑦ 测试结果

前置增强..........
save running.....

3.2 注解配置 AOP 详解
(1)注解通知的类型
通知的配置语法:@通知注解(“切点表达式")
在这里插入图片描述
(2) 切点表达式的抽取
同 xml 配置 aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut 注解定义切点表达式,然后在在增强注解中进行引用。具体如下:

@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {
    //Proceeding JoinPoint:  正在执行的连接点===切点
    //@Around("execution(* com.itheima.anno.*.*(..))")
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强....");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强....");
        return proceed;
    }

    //@After("execution(* com.itheima.anno.*.*(..))")
    @After("MyAspect.pointcut()")
    public void after(){
        System.out.println("最终增强..........");
    }

    //定义切点表达式
    @Pointcut("execution(* com.itheima.anno.*.*(..))")
    public void pointcut(){}
}

3.3 知识要点
● 注解aop开发步骤
① 使用@Aspect标注切面类
② 使用@通知注解标注通知方法
③ 在配置文件中配置aop自动代理aop:aspectj-autoproxy/

● 通知注解类型
在这里插入图片描述

第十章 声明式事务控制

1.编程式事务控制相关对象(了解)
1.1 PlatformTransactionManager
PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。

在这里插入图片描述
注意:
PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc 或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager
Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager

1.2 TransactionDefinition
TransactionDefinition 是事务的定义信息对象,里面有如下方法:
————————————————
(1)事务隔离级别
设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。
● ISOLATION_DEFAULT
● ISOLATION_READ_UNCOMMITTED
● ISOLATION_READ_COMMITTED
● ISOLATION_REPEATABLE_READ
● ISOLATION_SERIALIZABLE

(2)事务传播行为
● REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
● SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
● MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
● REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
● NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
● NEVER:以非事务方式运行,如果当前存在事务,抛出异常
● NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
● 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
● 是否只读:建议查询时设置为只读

1.3 TransactionStatus
TransactionStatus 接口提供的是事务具体的运行状态,方法介绍如下。
在这里插入图片描述
1.4 知识要点
编程式事务控制三大对象
● PlatformTransactionManager
● TransactionDefinition
● TransactionStatus

平台对象操作事务行为,定义对象设置事务属性,状态对象反馈事务运行过程中的信息。换句话说,状态对象由管理器和事务信息共同决定的。
基于 XML 的声明式事务控制
2.1 什么是声明式事务控制
Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

声明式事务处理的作用
● 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可
● 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便

注意:Spring 声明式事务控制底层就是AOP。业务对象是切点,事务管理是增强(切面)。

2.2 声明式事务控制的实现
声明式事务控制明确事项:
● 谁是切点?
● 谁是通知?
● 配置切面?

① 引入tx命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">

② 配置事务增强

<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!--通知  事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--设置事务的属性信息-->
    <tx:attributes>
        <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
        <tx:method name="save" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
        <tx:method name="findAll" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
        <!--代表update开头的所有方法-->
        <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

③ 配置事务 AOP 织入

<!--配置事务的aop织入-->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:advisor>
</aop:config>

④ 测试事务控制转账业务代码

@Override
public void transfer(String outMan, String inMan, double money) {
	accountDao.out(outMan,money);
	int i = 1/0;
	accountDao.in(inMan,money);
}

2.3 切点方法的事务参数的配置

<!--事务增强配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

其中,tx:method 代表切点方法的事务参数的配置,例如:

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>

● name:切点方法名称
● isolation:事务的隔离级别
● propogation:事务的传播行为
● timeout:超时时间
● read-only:是否只读

2.4 知识要点
声明式事务控制的配置要点
● 平台事务管理器配置
● 事务通知的配置
● 事务aop织入的配置

3.基于注解的声明式事务控制
3.1 使用注解配置声明式事务控制
(1)编写 AccoutDao

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
    }

    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}

(2) 编写 AccoutService

@Repository
@Transactional//这里没有写参数,代表采用默认配置;这里声明事务,代表这个类的所有方法都会开启事务
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)//这里又定义了事务,则此方法以这个事务定义为准
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan, money);
        //int i = 1 / 0;
        accountDao.in(inMan, money);
    }
}

(3)编写 applicationContext.xml 配置文件

<!--之前省略datsSource、jdbcTemplate、平台事务管理器的配置-->
<!--组件扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>

<!--事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3.2 注解配置声明式事务控制解析
① 使用 @Transactional 在需要进行事务控制的类或是方法上修饰,注解可用的属性同 xml 配置方式,例如隔离级别、传播行为等。
② 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。
③ 使用在方法上,不同的方法可以采用不同的事务参数配置。
④ Xml配置文件中要开启事务的注解驱动<tx:annotation-driven />

3.3 知识要点
注解声明式事务控制的配置要点
● 平台事务管理器配置(xml方式)
● 事务通知的配置(@Transactional注解配置)
● 事务注解驱动的配置 tx:annotation-driven/

第十一章 MyBatis入门操作

1.2 原始jdbc操作的分析
原始jdbc开发存在的问题如下:
① 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
② sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变java代码。
③ 查询操作时,需要手动将结果集中的数据手动封装到实体中。插入操作时,需要手动将实体的数据设置到sql语句的占位符位置

应对上述问题给出的解决方案:
① 使用数据库连接池初始化连接资源
② 将sql语句抽取到xml配置文件中
③ 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射

1.3 什么是Mybatis
● mybatis 是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
● mybatis通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
● 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

Mybatis的快速入门
2.1 MyBatis开发步骤
① 添加MyBatis的坐标
② 创建user数据表
③ 编写User实体类
④ 编写映射文件UserMapper.xml
⑤ 编写核心文件SqlMapConfig.xml
⑥ 编写测试类

2.2 环境搭建
(1)导入MyBatis的坐标和其他相关坐标

<!--mybatis坐标-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.32</version>
</dependency>
<!--mysql驱动坐标-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<!--单元测试坐标-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!--日志坐标-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

(2)创建user数据表
(3)编写User实体

public class User {
	private int id;
	private String username;
	private String password;
	//省略get和set方法
}

(4)编写UserMapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
    <select id="findAll" resultType="com.itheima.domain.User">
        select * from user
    </select>
</mapper>

(5)编写MyBatis核心文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--数据源环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>

    <!--加载映射文件-->
    <mappers>
        <mapper resource="com/itheima/mapper/UserMapper.xml"></mapper>
    </mappers>
</configuration>

测试

//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session回话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作  参数:namespace+id
List<User> userList = sqlSession.selectList("userMapper.findAll");
//打印数据
System.out.println(userList);
//释放资源
sqlSession.close();

2.4 知识小结
MyBatis开发步骤:
① 添加MyBatis的坐标
② 创建user数据表
③ 编写User实体类
④ 编写映射文件UserMapper.xml
⑤ 编写核心文件SqlMapConfig.xml
⑥ 编写测试类

3.MyBatis的映射文件概述
在这里插入图片描述
4.MyBatis的增删改查操作
4.1 MyBatis的插入数据操作
(1)编写UserMapper映射文件

<!--插入操作-->
<insert id="save" parameterType="com.itheima.domain.User">
    insert into user values(#{id},#{username},#{password})
</insert>

(2)编写插入实体User的代码

//模拟User对象
User user = new User();
user.setUsername("xxx");
user.setPassword("root");
//获取核心配置文件
InputStream resourceAsStream = Resource.getResourceAsStream("sqlMapConfig.xml");
//获得SqlSession工厂对象
SqlSessionFactory sqlSession = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession salSession = sqlSessionFactory.openSession();
//执行操作
sqlSession.insert("userMapper.save",user);
//mybatis执行更新操作,提交事务
sqlSession.commit();
//释放资源
sqlSession.close();

(3)插入操作注意问题
● 插入语句使用insert标签
● 在映射文件中使用parameterType属性指定要插入的数据类型
● Sql语句中使用#{实体属性名}方式引用实体中的属性值
● 插入操作使用的API是sqlSession.insert(“命名空间.id”,实体对象);
● 插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务,即sqlSession.commit()

4.2 MyBatis的修改数据操作
(1)编写UserMapper映射文件

<!--修改操作-->
<update id="update" parameterType="com.itheima.domain.User">
    update user set username=#{username},password=#{password} where id=#{id}
</update>

(2)编写修改实体User的代码

//模拟user对象
User user = new User();
user.setId(0);
user.setUsername("lucy");
user.setPassword("123");
//获得核心配置文件,"sqlMapConfig.xml"位置是相对于类加载路径的,如果是Maven工程,即相对于Resource文件路径的
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session回话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作  参数:namespace+id
sqlSession.update("userMapper.update", user);
//mybatis执行更新操作  提交事务
sqlSession.commit();
//释放资源
sqlSession.close();

(3)修改操作注意问题
● 修改语句使用update标签
● 修改操作使用的API是sqlSession.update(“命名空间.id”,实体对象);

4.3 MyBatis的删除数据操作

(1)编写UserMapper映射文件

<!--删除操作-->
<delete id="delete" parameterType="java.lang.Integer">
    delete  from user where id=#{id}
</delete>

(2)编写删除数据的代码

//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session回话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作  参数:namespace+id
sqlSession.delete("userMapper.delete", 0);
//mybatis执行更新操作  提交事务
sqlSession.commit();
//释放资源
sqlSession.close();

(3)删除操作注意问题
● 删除语句使用delete标签
● Sql语句中使用#{任意字符串}方式引用传递的单个参数
● 删除操作使用的API是sqlSession.delete(“命名空间.id”,Object);

4.4 知识小结
增删改查映射配置与API:
在这里插入图片描述
5. MyBatis核心配置文件概述

5.1 MyBatis核心配置文件层级关系
在这里插入图片描述
5.2 MyBatis常用配置解析
(1)environments标签
数据库环境的配置,支持多环境配置
在这里插入图片描述
其中,事务管理器(transactionManager)类型有两种:
● JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
● MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。

其中,数据源(dataSource)类型有三种:
● UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
● POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
● JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
(2)mapper标签
该标签的作用是加载映射的,加载方式有如下几种:
● 使用相对于类路径的资源引用,例如:
● 使用完全限定资源定位符(URL),例如:
● 使用映射器接口实现类的完全限定类名,例如:
● 将包内的映射器接口实现全部注册为映射器,例如:
(3)Properties标签
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件
在这里插入图片描述
(4) typeAliases标签
类型别名是为Java 类型设置一个短的名字。原来的类型名称配置如下
在这里插入图片描述
上面我们是自定义的别名,mybatis框架已经为我们设置好的一些常用的类型的别名:
在这里插入图片描述
5.3 知识小结
核心配置文件常用配置:
(1)properties标签:该标签可以加载外部的properties文件

<properties resource="jdbc.properties"></properties>

(2)typeAliases标签:设置类型别名

<typeAliases>
	<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
</typeAliases>

(3)mappers标签:加载映射配置(在mybatis核心配置文件中引用)

<mappers>
	<mapper resource="com/itheima/mapper/UserMapper.xml"></mapper>
</mappers>

(4)environments标签:数据源环境配置标签(在mybatis核心配置文件中引用)

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc. url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>

第十二章 MyBatis的Dao层实现方式

1.1 传统开发方式
1.2 代理开发方式
(1) 代理开发方式介绍
采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper 接口开发需要遵循以下规范:
① Mapper.xml文件中的namespace与mapper接口的全限定名相同
② Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
③ Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
④ Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

(2)编写UserMapper接口

在这里插入图片描述
(3)测试代理方式

@Test
public void testProxyDao() throws IOException {
	InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
	SqlSession sqlSession = sqlSessionFactory.openSession();
	//获得MyBatis框架生成的UserMapper接口的实现类
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	User user = userMapper.findById(1);
	System.out.println(user);
	sqlSession.close();
}

第十三章 MyBatis映射文件深入

MyBatis映射文件深入
1.1 动态sql语句
(1)动态sql语句概述
Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。
(2)动态 SQL 之
我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。

<select id="findByCondition" parameterType="user" resultType="user">
    select * from user
    <where>
        <if test="id!=0">
            and id=#{id}
        </if>
        <if test="username!=null">
            and username = #{username}
        </if>
        <if test="password!=null">
            and password = #{password}
        </if>
    </where>
</select>

当查询条件id和username都存在时,控制台打印的sql语句如下:

… … …
//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User condition = new User();
condition.setId(1);
condition.setUsername("lucy");
User user = userMapper.findByCondition(condition);
… … …

(3)动态 SQL 之
循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)。

<select id="findByIds" parameterType="list" resultType="user">
    select * from user
    <where>
        <foreach collection="list" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>

测试代码片段如下:

… … …
//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int[] ids = new int[]{2,5};
List<User> userList = userMapper.findByIds(ids);
System.out.println(userList);
… … …

在这里插入图片描述
foreach标签的属性含义如下:
标签用于遍历集合,它的属性:
● collection:代表要遍历的集合元素,注意编写时不要写#{}
● open:代表语句的开始部分
● close:代表结束部分
● item:代表遍历集合的每个元素,生成的变量名
● sperator:代表分隔符
1.2 SQL片段抽取
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
在这里插入图片描述
1.3 知识小结
MyBatis映射文件配置:
:查询
:插入
:修改
:删除
:where条件
:if判断
:循环
:sql片段抽取

第十四章 MyBatis核心配置文件深入

1.MyBatis核心配置文件深入
1.1 typeHandlers标签
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。

在这里插入图片描述
可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。

开发步骤:
① 定义转换类继承类BaseTypeHandler
② 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
③ 在MyBatis核心配置文件中进行注册
④ 测试转换是否正确

public class MyDateTypeHandler extends BaseTypeHandler<Date> {
    //将java类型转成数据库需要的类型
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType type) {
        preparedStatement.setString(i, date.getTime() + "");
    }
    //下面三个是将数据库的类型转成java类型
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return new Date(resultSet.getLong(s));
    }
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return new Date(resultSet.getLong(i));
    }
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return callableStatement.getDate(i);
    }
}
<!--MyBatis核心配置文件中 注册类型自定义转换器-->
<typeHandlers>
    <typeHandler handler="com.itheima.typeHandlers.MyDateTypeHandler"></typeHandler>
</typeHandlers>

测试添加操作:

user.setBirthday(new Date());
userMapper.add2(user);

1.2 plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
① 导入通用PageHelper的坐标
② 在mybatis核心配置文件中配置PageHelper插件
③ 测试分页数据获取

① 导入通用PageHelper坐标

<!-- 分页助手 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.7.5</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>0.9.1</version>
</dependency>

② 在mybatis核心配置文件中配置PageHelper插件

<!-- 注意:分页助手的插件 配置在通用馆mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
    <!-- 指定方言 -->
    <property name="dialect" value="mysql"/>
</plugin>

③ 测试分页代码实现

@Test
public void testPageHelper() {
    //设置分页参数
    PageHelper.startPage(1, 2);
    List<User> select = userMapper2.select(null);
    for (User user : select) {
        System.out.println(user);
    }
}

获得分页相关的其他参数

//其他分页的数据
PageInfo<User> pageInfo = new PageInfo<User>(select);
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("每页显示长度:" + pageInfo.getPageSize());
System.out.println("是否第一页:" + pageInfo.isIsFirstPage());
System.out.println("是否最后一页:" + pageInfo.isIsLastPage());

1.3 知识小结
MyBatis核心配置文件常用标签:
(1)properties标签:该标签可以加载外部的properties文件
(2)typeAliases标签:设置类型别名
(3)environments标签:数据源环境配置标签
(4)typeHandlers标签:配置自定义类型处理器
(5)plugins标签:配置MyBatis的插件

第十五章 MyBatis的多表操作

Mybatis多表查询
1.1 一对一查询
(1)一对一查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
在这里插入图片描述
(2)一对一查询的语句
对应的sql语句:select *,o.id oid from orders o,user u where o.uid=u.id;
查询的结果如下:
在这里插入图片描述
(3)创建Order和User实体在这里插入图片描述
(4)创建OrderMapper接口

public interface OrderMapper {
    //查询全部的方法
    public List<Order> findAll();

}

(5)配置OrderMapper.xml

<mapper namespace="com.itheima.mapper.OrderMapper">
    <resultMap id="orderMap" type="order">
        <!--手动指定字段与实体属性的映射关系
            column: 数据表的字段名称
            property:实体的属性名称-->
        <id column="oid" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="total" property="total"></result>
        <result column="uid" property="user.id"></result>
        <result column="username" property="user.username"></result>
        <result column="password" property="user.password"></result>
        <result column="birthday" property="user.birthday"></result>
    </resultMap>
    <select id="findAll" resultMap="orderMap">
         select *,o.id oid from orders o,user u where o.uid=u.id;
    </select>
</mapper>

其中还可以配置如下:

<mapper namespace="com.itheima.mapper.OrderMapper">
    <resultMap id="orderMap" type="order">
        <!--手动指定字段与实体属性的映射关系
            column: 数据表的字段名称
            property:实体的属性名称-->
        <id column="oid" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="total" property="total"></result>
        <association property="user" javaType="user">
            <id column="uid" property="id"></id>
            <result column="username" property="username"></result>
            <result column="password" property="password"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>
    <select id="findAll" resultMap="orderMap">
         select *,o.id oid from orders o,user u where o.uid=u.id;
    </select>
</mapper>

(6)测试

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Order> all = mapper.findAll();
for(Order order : all){
	System.out.println(order);
}

在这里插入图片描述
1.2 一对多查询
(1)一对多查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
在这里插入图片描述
(2) 一对多查询的语句
对应的sql语句:select *,o.id oid from user u left join orders o on u.id=o.uid;
查询的结果如下:
在这里插入图片描述
(3)修改User实体
在这里插入图片描述
(4)创建UserMapper接口

public interface UserMapper {
    public List<User> findAll();
}

(5)配置UserMapper.xml

<mapper namespace="com.itheima.mapper.UserMapper">
    <resultMap id="userMap" type="user">
        <id column="uid" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="birthday" property="birthday"></result>
        <!--配置集合信息
            property:集合名称
            ofType:当前集合中的数据类型-->
        <collection property="orderList" ofType="order">
            <!--封装order的数据-->
            <id column="oid" property="id"></id>
            <result column="ordertime" property="ordertime"></result>
            <result column="total" property="total"></result>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="userMap">
        SELECT *,o.id oid FROM USER u,orders o WHERE u.id=o.uid
    </select>
</mapper>

(6)测试

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for(User user : all){
	System.out.println(user.getUsername());
	List<Order> orderList = user.getOrderList();
	for(Order order : orderList){
		System.out.println(order);
	}
	System.out.println("----------------------------------");
	}

在这里插入图片描述
1.3 多对多查询
(1)多对多查询的模型
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
在这里插入图片描述
在这里插入图片描述
(4)添加UserMapper接口方法

public List<User> findUserAndRoleAll();

(5)配置UserMapper.xml

<resultMap id="userRoleMap" type="user">
    <!--user的信息-->
    <id column="userId" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
    <result column="birthday" property="birthday"></result>
    <!--user内部的roleList信息-->
    <collection property="roleList" ofType="role">
        <id column="roleId" property="id"></id>
        <result column="roleName" property="roleName"></result>
        <result column="roleDesc" property="roleDesc"></result>
    </collection>
</resultMap>

<select id="findUserAndRoleAll" resultMap="userRoleMap">
    SELECT * FROM USER u,sys_user_role ur,sys_role r WHERE u.id=ur.userId AND ur.roleId=r.id
</select>

(6)测试

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAllUserAndRole();
for(User user : all){
	System.out.println(user.getUsername());
	List<Role> roleList = user.getRoleList();
	for(Role role : roleList){
		System.out.println(role);
	}
	System.out.println("----------------------------------");
}

在这里插入图片描述
1.4 知识小结
MyBatis多表配置方式:
一对一配置:使用做配置
一对多配置:使用+做配置
多对多配置:使用+做配置

第十六章 MyBatis注解开发

Mybatis的注解开发
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper
映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装

1.2 MyBatis的增删改查
我们完成简单的user表的增删改查的操作

public class MyBatisTest {
    private UserMapper mapper;
    @Before
    public void before() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        mapper = sqlSession.getMapper(UserMapper.class);
    }
    @Test
    public void testSave(){
        User user = new User();
        user.setUsername("tom");
        user.setPassword("abc");
        mapper.save(user);
    }
    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(18);
        user.setUsername("lucy");
        user.setPassword("123");
        mapper.update(user);
    }
    @Test
    public void testDelete(){
        mapper.delete(18);
    }
    @Test
    public void testFindById(){
        User user = mapper.findById(2);
        System.out.println(user);
    }
    @Test
    public void testFindAll(){
        List<User> all = mapper.findAll();
        for (User user : all) {
            System.out.println(user);
        }
    }
}

修改MyBatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可

<mappers>
	<!--扫描使用注解的类-->
	<mapper class="com.itheima.mapper.UserMapper"></mapper>
</mappers>

或者指定扫描包含映射关系的接口所在的包也可以

<!--加载映射关系-->
<mappers>
    <!--指定接口所在的包-->
    <package name="com.itheima.mapper"></package>
</mappers>

UserMapper改为注解形式

public interface UserMapper {
    @Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
    public void save(User user);

    @Update("update user set username=#{username},password=#{password} where id=#{id}")
    public void update(User user);

    @Delete("delete from user where id=#{id}")
    public void delete(int id);

    @Select("select * from user where id=#{id}")
    public User findById(int id);

    @Select("select * from user")
    public List<User> findAll();
}

1.3 MyBatis的注解实现复杂映射开发
实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置
在这里插入图片描述
1.4 一对一查询
(1) 一对一查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
在这里插入图片描述

(2)一对一查询的语句
第一种方式:
对应的sql语句:
select *,o.id oid from orders o,user u where o.uid=u.id
查询的结果如下:
在这里插入图片描述

第二种方式:
select * from orders;
select * from user where id=查询出订单的uid;

(3)创建Order和User实体
在这里插入图片描述

(4)创建OrderMapper接口

public interface OrderMapper {
	public List<Order> findAll();
}

(5)使用注解配置Mapper

public interface OrderMapper {
	@Select("select *,o.id oid from orders o,user u where o.uid=u.id")
	@Results({
	        @Result(column = "oid",property = "id"),
	        @Result(column = "ordertime",property = "ordertime"),
	        @Result(column = "total",property = "total"),
	        @Result(column = "uid",property = "user.id"),
	        @Result(column = "username",property = "user.username"),
	        @Result(column = "password",property = "user.password")
	})
	public List<Order> findAll();
}
public interface UserMapper {
    @Select("select * from user")
    public List<User> findAll();
}

1.5 一对多查询
(1)一对多查询的模型
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
在这里插入图片描述
(2)一对多查询的语句
对应的sql语句:
select * from user;
select * from orders where uid=查询出用户的id;
查询的结果如下:
在这里插入图片描述
(3)修改User实体
在这里插入图片描述
(4)创建UserMapper接口

public List<User> findUserAndOrderAll();

(5)使用注解配置Mapper

public interface UserMapper {
@Select(“select * from user”)
@Results({
@Result(id=true ,column = “id”,property = “id”),
@Result(column = “username”,property = “username”),

            @Result(column = "password",property = "password"),
            @Result(
                    property = "orderList",
                    column = "id",
                    javaType = List.class,
                    many = @Many(select = "com.itheima.mapper.OrderMapper.findByUid")
            )
    })
    public List<User> findUserAndOrderAll();
}
public interface OrderMapper {
    @Select("select * from orders where uid=#{uid}")
    public List<Order> findByUid(int uid);
}

(6)测试结果

List<User> all = userMapper.findAllUserAndOrder();
for(User user : all){
	System.out.println(user.getUsername());
	List<Order> orderList = user.getOrderList();
	for(Order order : orderList){
		System.out.println(order);
	}
	System.out.println("-----------------------------");
}

在这里插入图片描述
1.6 多对多查询
(1)多对多查询的模型
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
在这里插入图片描述
(2)多对多查询的语句
对应的sql语句:
select * from user;
select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=用户的id
查询的结果如下:
在这里插入图片描述
(3)创建Role实体,修改User实体
在这里插入图片描述
(4)添加UserMapper接口方法

public List<User> findUserAndRoleAll();

(5)使用注解配置Mapper

public interface UserMapper {
    @Select("SELECT * FROM USER")
    @Results({
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "username",property = "username"),
            @Result(column = "password",property = "password"),
            @Result(
                    property = "roleList",
                    column = "id",
                    javaType = List.class,
                    many = @Many(select = "com.itheima.mapper.RoleMapper.findByUid")
            )
    })
    public List<User> findUserAndRoleAll();
}
public interface RoleMapper {
    @Select("SELECT * FROM sys_user_role ur,sys_role r WHERE ur.roleId=r.id AND ur.userId=#{uid}")
    public List<Role> findByUid(int uid);
}

第十七章 SSM整合

见“第十七天资料”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值