SMM(Spring+SpringMVC+MyBatis)

Spring & SpringMVC & MyBatis

一、Spring的体系结构

自下往上:

  • Test
  • Core Container 核心容器
    • Beans :容器
    • Core :核心
    • Context :上下文
    • spEL :Spring表达式
  • AOP----Aspects----Instrumentation----Messaging
  • Data Access/Integration 数据访问层
    • JDBC
    • ORM
    • OXM
    • JMS
    • Transactions
  • Web
    • WebSocket
    • Servlet
    • Web
    • Portlet

二、一般创建流程

  1. 导入坐标
  2. UserDao接口
  3. UserDaolmpl实现
  4. applicationContext.xml 创建配置文件,将当前文件配置到配置文件()
  5. Application文件 getBean(" {id} ")获取对象

三、Spring配置文件

3.1 Bean标签的基本配置

  • id 唯一标识 不允许重复
  • class 路径 全限定名
注意点: Bean内部存在无参构造

3.2 Bean标签范围配置

scope
取值范围说明生命周期
singleton默认的,单例的对象创建:应用加载,创建容器时对象就被创建了 对象运行:只要容器在,对象一直活着 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
prototype多例的对象创建:当使用对象时,创建新的对象实例 对象运行:只要对象在使用中,就一直活着 对象销毁:当对象长时间不用时,被Java的垃圾回收器回收了
requestWEB项目中,Spring创建一个Bean的对象,将对象存到request域中
sessionWEB项目中,Spring创建一个Bean的对象,将对象存到session域中
global sessionWEB项目中,应用在Portlet环境中,如果没有Portlet环境,那么globalSession相当于session

3.3 Bean生命周期配置

  • init-method 指定类中的初始化方法名称
  • destroy-method 指定类中销毁方法名称

3.4 Bean实例化三种方式

  • 无参构造方法 Important
  • 工厂静态方法实例化 factory-method
  • 工厂实例方法实例化 配两个Bean 写工厂类 类中方法返回实例化值
<bean id="factory" class="com.ithemima.factory.DynamicFactory"></bean>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao" ></bean>

3.5 Bean的依赖注入

set方法 Service内部需要Dao 需要文件配置

<bean>
  <property name="userDao" ref=""  value="" ></property>
  (u大小转成小写 对应Service中setUserDao)
  (ref 对象类型引用)
  (value 普通数据类型)
</bean>

如果是集合类型 以 List<String>为例

<bean>
  <property>
    <list>
      <value>xxx</value>
      <value>xxx</value>
      
    </list>
  </property>
</bean>

Map<String, Object>

<bean>
  <property>
    <Map>
        <entry key="" value-ref="{Bean的id}"></entry>
    </Map>
  </property>
</bean>

3.6 知识要点

Spring的重点配置 文档
内容含义
<bean>标签
id属性在容器中Bean实例的唯一标识,不允许重复
class属性要实例化的Bean的全限定名
scope属性Bean的作用范围,常用是Singleton(默认)和prototype
<proterty>属性属性注入
name属性属性名称
value属性注入的普通属性值
ref属性注入的对象引用值
<list>标签
<Map>标签
<properties>标签
<constructor-arg>有参构造注入
<import>标签导入其他的Spring的分文件

四、Spring相关API

4.1 ApplicationContext的实现类

  1. ClassPathXmlApplicationContext 从类的根路径(resource)下加载配置文件
  2. FileSystemXmlApplicationContext 从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
  3. AnnotationConfigApplicationContext 使用注解配置容器对象时,需要使用此类来创建Spring容器,它用来读注解

4.2 getBean()方法使用

  1. 传一个String类型数据 id 可以获取相同类型的数据
  2. 传一个<T> 如xxx.class 返回值为该类 不能获取多个相同类型的数据

4.3 知识要点

Spring的重点API
ApplicationContext app = new ClasspathXmlApplicationContext("xml文件");
app.getBean("id");
app.getBean(Class);

Spring配置数据源

1.1 数据源(连接池)的作用

  • 数据源(连接池)是提高程序性能的
  • 事先实例化数据源,初始化部分连接资源
  • 使用连接资源时从数据源中获取
  • 使用完毕后将连接资源还给数据源
    常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid

1.2 数据源的开发步骤

  1. 导入数据源的坐标和数据库驱动坐标
  2. 创建数据源对象
  3. 设置数据源的基本连接数据
  4. 使用数据源获取连接资源和归还资源

1.3 数据源的手动创建

pom中 dependencies配置

ComboPoolDataSource dataSource = new ComboPoolDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/");
dataSource.setUser("root");
dataSource.setPassword("root");
Connection connection = dataSource.getConnection();//连接数据源
connection.close();//归还数据源

解耦合 读取配置文件

ResourceBundle = ResourceBundle.getBundle("jdbc");//resource下jdbc.properties文件
String driver = rb.getString("jdbc.driver");//Key
String url = rb.getString("jdbc.url");
String username = rb.getString("jdbc.username");
String password = rb.getString("jdbc.password");

ComboPoolDataSource dataSource = new ComboPoolDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);

Connection connection = dataSource.getConnection();//连接数据源

connection.close();//归还数据源

1.4 Spring 配置数据源

先配置

ApplicationContext app = new ApplicationCOntext();
DataSource dataSource =  app.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
connection.close();

1.5 知识要点

Spring容器加载properties文件


<context:property-placeholder location="xx.properties"></context:property-placeholder>
<property name="" value="${key}" />

Spring注解开发

2.1 Spring原始注解

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

注解说明
@Component使用在类上实例化Bean
@Controller使用在Web层类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用,用于根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

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

<context:component-scan base-package=""></context:component-scan>

2.2 Spring新注解 文档

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:

  • 非自定义的Bean配置 (如第三方规定的)
  • 加载properties文件的配置
  • 组件扫描的配置
  • 引入其他文件的配置
注解说明
@Configuration用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定Spring在初始化容器时要扫描的包
作用和在Spring的xml配置文件中的
<context:component-scan base-package="com.itheima"/>一样
@Bean使用在方法上,标注将该方法的返回值存储到Spring容器中
@PropertySource用于加载.properties文件中的配置
@Import用于导入其他配置类

2.3 使用Junit进行测试

2.4 Spring监听器

Spring提供了一个监听器ContextLoaderListener,对获取上下文功能进行封装,该监听器
内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象

SpringMVC

SpringMVC快速入门

需求:客户端发起请求,服务器端接受请求,执行逻辑并进行视图跳转
开发步骤:

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

流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4yXtMMR1-1632051628434)(C:\Users\Superclass\IdeaProjects\demo01\src\main\resources\SpingLearn\SpringMVC.jpg “SpringMVC”)]

SpringMVC执行流程

  1. 用户发请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
  3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
  4. DispatcherServlet调用HandlerAdapter处理器适配器
  5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
  6. Controller执行完返回ModelAndView
  7. HandlerAdaptercontroller执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServletModelAndView传给ViewResolver视图解析器
  9. ViewResolver解析后返回具体View
  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet相应用户

SpringMVC注解

@RequestMapping

作用:用于建立请求URL和处理请求方法之间的对应关系
位置:

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/user")
public class UserController{
    
    //访问路径为 http://localhost:8080/user/save
    @RequestMapping("/save")
    public String save(){
        return "/success.jsp"; //注意需要`/` 否则路径为相对路径会找不到资源
    }
}

属性:

  • value 用于指定请求的URL,它和path属性的作用是一样的
  • method 用于指定请求的方式(四种,但是form只能回get/post
  • params 用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样

SpringMVC知识要点

SpringMVC相关组件

  • 前端控制器 DispatcherServlet
  • 处理器映射器 HandlerMapping
  • 处理器适配器 HandlerAdapter
  • 处理器 Handler
  • 视图解析器 ViewResolver
  • 视图 View

SpringMVC注解和配置

  • 请求映射注解 @RequestMapping
  • 视图解析器配置
  REDIRECT_URL_PREFIX="redirect:"
  FORWARD_URL_PREFIX="forward:"
  prefix="";
  suffix="";

SpringMVC数据响应

1.1 SpringMVC的数据响应方式

1) 页面跳转

  • 直接返回字符串
  • 通过ModelAndView对象返回
    2) 回写数据
  • 直接返回字符串
  • 返回对象或集合

1.2 页面跳转

1. 返回字符串形式
@RequestMapping("/quick")
public String quickMethod(){
    return "index";
}
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/views" /> <!--路径前缀-->
  <property name="suffix" value=".jsp" /> <!--路径后缀-->
</bean>

转发地址 /WEB-INF/views/index.jsp

2. 返回ModelAndView对象
@RequestMapping("/quick")
public ModelAndView quickMethod(){
    /*
     * Model: 模型 作用:封装数据
     * View: 视图 作用:展示数据
     */
    ModelAndView modelAndView = new ModelAndView();
    //设置模型数据
    modelAndView.addObject("username","itcast"); //任意数据   
    //设置视图
        modelAndView.setViewName("index");    
    return modelAndView;
}

参数为ModelAndView

@RequestMapping("/quick2")
public ModelAndView quickMethod(ModelAndView modelAndView){ //注入
    /*
     * Model: 模型 作用:封装数据
     * View: 视图 作用:展示数据
     */
    //设置模型数据
    modelAndView.addObject("username","itcast"); //任意数据   
    //设置视图
        modelAndView.setViewName("index");    
    return modelAndView;
}

ModelAndView可以拆开,单取Model

1.3 回写数据

1. 直接返回字符串

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

@RequestMapping("/quick3")
public void save(HttpServletResponse response) throws IOException { //回写
    response.getWriter().print("hello itcast");
}

如何解耦合
需要@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转,是直接在http响应体中返回

@RequestMapping("/quick3")
@ResponseBody //回写就要加
public void save(){
    return "hello itcast";
}

如何返回json数据

@RequestMapping("/quick3")
@ResponseBody //回写就要加
public void save(){
    User user = new User();
    user.setUsername("李四");
    user.setAge(30);
    ObjectMapper objectMapper = new ObjectMapper();
    String json = objectMapper.writeValueAsString(user);
    
    return json;
}

手动转很累

2. 返回对象或集合
<!--配置处理器映射器 RequestMappingHandlerAdapter -> messageConverters -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
  <property name="messageConverters">
    <list> <!--内部方法为 public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters)-->
      <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <!-- 2=to -->
        
      </bean>
    </list>
  </property>
</bean>
@RequestMapping("/quick3")
@ResponseBody //回写就要加
public User save(){ //利用[适配器]
    user.setUsername("李四");
    user.setAge(30);

    return user;
}

可以使用mvc的注解驱动代替上述xml配置

<!--mvc的注解驱动-->
<mvc:annotation-driven xmlns="http://www.springframework.org/schema/cache"></mvc:annotation-driven>

1.4 知识要点

  1. 直接页面跳转
  • 直接返回字符串 --视图跳转
    • forward: 转发
    • redirect 重定向
  • 返回ModelAndView对象返回
    • Model 数据模型
    • View 视图
  1. 回写数据
  • 直接返回字符串
  • 返回对象或集合

SpringMVC获取请求数据

1. 获取请求参数

客户端请求参数的格式是: name=value&name=value...GET
服务器端要获取请求的参数,有时还需要进行数据的封装,SpringMVC可以接受接收如下类型的参数:

  • 基本类型参数
  • POJO类型参数
  • 数组类型参数
  • 集合类型参数

2.获得基本类型参数

Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配

@RequestMapping("/quick4")
@ResponseBody //回写就要加
public void userParams(String name, int age){
    System.out.println(name);
    System.out.println(age);
}

3.获得POJO类型参数

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

@RequestMapping("/quick5") 
@ResponseBody 
public void userParams(User user){
 System.out.println(user);    
}

4.获取数组类型参数

Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射

@RequestMapping("/quick5")
@ResponseBody
public void userParams(String[] strs){
 System.out.println(Arrays.asList(strs));    
}

5.获得集合类型参数

获得集合参数时,要将集合参数包装到一个POJO中才可以
当使用ajax提交时,可以指定contenttype为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>Title</title>
  <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
  <script>
    var userList = new Array();
    userList.push({username:"张三",age:18});
    userList.push({username:"李四",age:20});
    
    $.ajax({
      type:"POST",
      url:"${pageContext.request.contextPath}/user/quick6",
      data:JSON.stringify(userList),
      contentType:"application/json;charset=utf-8"
    });
  </script>
</head>
<body>

</body>
</html>
import org.springframework.web.bind.annotation.ResponseBody;

@RequestMapping("/quick6")
@ResponseBody
public void userParams(@ResponseBody List < User > userList)throws IOException{
    System.out.println(userList);
}

mvc配置


<!--开发资源的访问-->
<mvc:resources mapping="/js/**" location="/js/"/>

或者

<mvc:default-servlet-handler/>

6.请求数据乱码问题

当POST请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤
配置filter

<filter>
  <filter-name>
    CharacterEncodingFilter
  </filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

7.参数绑定注解@requestParam

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

<form action="${pagaContext.request.contextPath}/test" method="post">
  <input type="text" name="name"><br>
  <input type="submit" value="提交"><br>
</form>
@RequestMapping("/quick6")
@ResponseBody
public void userParams(@RequestParam("name") String username)throws IOException{
    System.out.println(username);
}

注解@RequestParam有如下参数可以使用

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

8.获得Restful风格的参数

Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束。主要用于客户端和服务端交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制。
Restful风格的请求是使用“URL+请求方式”表示一次请求目的的,HTTP协议里面四个表示操作方式的动词如下:

  • GET 用于获取资源
  • POST 用于新建资源
  • PUT 用于更新资源
  • DELETE 用于删除资源
    在SpringMVC中可以使用占位符进行参数绑定。如将/user/11可以写成/user/{name},占位符{name}对应的就是1的值。在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作
import org.springframework.web.bind.annotation.PathVariable;

@RequestMapping("/user/{name}")
@ResponseBody
public void userParams(@PathVariable(value="name", required = true) String name){
        System.out.println(name);
}

9.自定义类型转换器

自定义类型转换器的开发步骤

  1. 定义转换器类实现Converter接口
  2. 在配置文件中声明转换器
  3. 在中引用转换器
import javax.persistence.Converter;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateConverter implements Converter<String, Date> {
  public Date converter(String dateStr) {
    //将日期字符串转换成日期对象
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    Date date = simpleDateFormat.parse(dateStr);
    return date;
  }
}
<mvc:annotation-drivern conversion-services="conversionService"></mvc:annotation-drivern> 
<bean class="org.springframework.context.support.ConversionServiceFactoryBean">
  <property name="converters">
    <list>
      <bean class="{当前路径}.converters.DateConverters"></bean>
    </list>
  </property>
</bean>

10.获得Servlet相关API

SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession

11.获得请求头

1.@RequestHeader

使用@RequestHeader可以获得请求头信息,相当于web中学习的request.getHeader(name)
@RequestHeader注解的属性如下:

  • value 请求头的名称
  • required 是否必须携带该请求头
import org.springframework.web.bind.annotation.RequestHeader;

@RequestMapping("/user/requestheader")
@ResponseBody
public void userParams(@RequestHeader(value = "User-Agent") String user_agent){
        System.out.println(user_agent);
}

2.@CookieValue

使用@CookieValue可以获得指定Cookie的值
@CookiValue注解的属性如下:

  • value 指定cookie的名称
  • required 是否必须携带该cookie

import org.springframework.web.bind.annotation.CookieValue;

@RequestMapping("/user/cookievalue")
@ResponseBody
public void userParams(@CookieValue(value = "JSESSIONID") String jsessionId){
        System.out.println(jsessionId);
        }

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取值为Multipart/form-data时,请求正文内容就变成多部分形式
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-94oDmzFL-1632051628438)(C:\Users\Superclass\IdeaProjects\demo01\src\main\resources\SpingLearn\fileUpload.png)]

3. 单文件上传步骤

  1. 导入fileuoload和io坐标
  2. 配置文件上传解析器
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <property name="defaultEncoding" value="UTF-8"></property>
  <property name="maxUploadSource" value="500000"></property>
</bean>
  1. 编写文件上传代码
@RequestMapping("/user/file")
@ResponseBody
public void userParams(MultipartFile uploadFile) throws IOException{//参数名要对应
    String originalFilename = uploadFile.getOriginalFilename();//获得原始文件名称
    uploadFile.transferTo(new File("C:\\upload\\"+originalFilename));//存文件
}

4.多文件上传

@RequestMapping("/user/file")
@ResponseBody
public void userParams(MultipartFile[] uploadFiles) throws IOException{//参数名要对应
    for(MultipartFile file:uploadFile){
        String originalFilename = file.getOriginalFilename();//获得原始文件名称
        file.transferTo(new File("C:\\upload\\"+originalFilename));//存文件
    }

}

14.知识要点

MVC实现数据请求方式

  • 基本类型参数
  • POJO类型参数
  • 数组类型参数
  • 集合类型参数
    MVC获取数据细节
  • 中文乱码问题
  • @RequestParam@PathVariable
  • 自定义类型转换器
  • 获得Servlet相关API
  • @RequestHeader@CookieValue
  • 文件上传

JDBC

1. Spring JDBCTemplate基本使用

1.1 JdbcTemplate概述

他是spring框架中提供的一个对象,是对原始繁锁的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和HibernateTempalet,操作
nosql数据的RedisTemplate,操作消息队列的JmsTemplate等等。

1.2 JdbcTemplate开发步骤

  1. 导入spring-jdbc和spring-tx坐标
  2. 创建数据库表和实体
public class Account{
  private String name;
  private double money;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public double getMoney() {
    return money;
  }

  public void setMoney(double money) {
    this.money = money;
  }

  @Override
  public String toString() {
    return "Account{" +
            "name='" + name + '\'' +
            ", money=" + money +
            '}';
  }
}
  1. 创建JdbcTemplate多谢
import org.springframework.jdbc.core.JdbcTemplate;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcTemplateTest {

  @Test
  public void test01() {
    //创建数据源对象
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setDriverClass("com.mysql.jdbc.Driver");
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
    dataSource.setUser("root");
    dataSource.setPassword("root");

    JdbcTemplate jdbcTemplate = new JdbcTemplate();
    //设置数据源对象 知道数据库在哪(Connection)
    jdbcTemplate.setDataSource(dataSource);

    //执行操作
    int row = jdbcTemplate.update("insert into account values(?,?)", "tom", 5000);
    System.out.println(row);
  }
}
  1. 执行数据库操作

1.3 Spring产生JdbcTemplate对象

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  <property name="driverClass" value="com.mysql.jdbc.Driver" />
  <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" />
  <property name="user" value="root" />
  <property name="password" value="root" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
</bean>
import org.apache.catalina.core.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcTemplateTest {

  @Test
  public void test02() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);
    
    int row = jdbcTemplate.update("insert into account values(?,?)", "tom", 5000);
    System.out.println(row);
  }
}

1.4 JdbcTemplate的常用操作

数据库语句不同
CRUD都是update方法

import com.example.demo01.reflect.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {
  @Autowired
  private JdbcTemplate jdbcTemplate;
  
  @Test
  public void testUpdate(){
      jdbcTemplate.update("update account set money=? where name=?",10000,"tom");
  }
  @Test
  public void testDelete(){
    jdbcTemplate.update("delete from account where name=?","tom");
  }
}

查询

import com.example.demo01.beans.Account;
import com.example.demo01.reflect.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {
  @Autowired
  private JdbcTemplate jdbcTemplate;

  //查一个
  @Test
  public void testQueryOne() {
    Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), "tom");
    System.out.println(account);
  }

  //全查
  @Test
  public void testQueryAll() {
    List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));//<实体泛型>(字节码)
    System.out.println(accountList);
  }

  //统计数量
  @Test
  public void testQueryCount() {
    Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
    System.out.println(count);
  }
}

SpringMVC 拦截器

1.1 拦截器(interceptor)的作用

SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或
字段时,拦截器中的拦截器会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。

1.2拦截器和过滤器的区别

区别过滤器(Filter)拦截器(Interceptor)
使用范围是Servlet规范中的一部分,任何Java Web工程都可以使用是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
拦截范围在url-pattern中配置了/*之后,可以对所有要访问的资源拦截在<mvc:mapping path=""/>中配置了/**之后,也可以对所有资源进行拦截,但是可以通过<mvc:exclude-mapping path:“”/>标签排除不需要拦截的资源

1.3拦截器快速入门

自定义拦截器有一下步骤:

  1. 创建拦截器类实现HandlerInterceptor接口
  2. 配置拦截器
  3. 测试拦截器的拦截效果

1.4实例

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
    //在目标方法执行前 执行
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    System.out.println("preHandler.....");
    String param = request.getParameter("param");
    if("yes".equals(param)){
        return true;
    }
    else {
      request.getRequestDispatcher("/error.jsp").forward(request,response);
      return false;//服务停止
    }
  }
    //在目标方法执行后 视图对象返回之前执行
  public void postHandler(HttpServletRequest request, HttpServletResponse, Object handler, ModelAndView modelAndView) {
    System.out.println("postHandler.....");
  }
    //在流程都执行完毕后执行
  public void after(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex){
    System.out.println("afterCompletion.....");
  }
}

配置拦截器


<mvc:interceptors xmlns="http://www.springframework.org/schema/mvc">
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="{path}.MyInterceptor1"
  </mvc:interceptor>
</mvc:interceptors>

Controller层

import org.springframework.web.servlet.ModelAndView;

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

}

1.5;拦截器方法说明

方法说明
preHandle()方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个INterceptor的preHandle方法
postHandle()该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回值渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作
afterCompletion()该方法在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用

SpringMVC异常处理

1.1异常处理的思路

系统中的异常包括两类:预期异常运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过gui’fan代码开发,测试等手段减少运行时异常的发生。
系统的DaoServiceController出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M5hUumy6-1632051628440)(C:\Users\Superclass\IdeaProjects\demo01\src\main\resources\SpingLearn\exception.png)]

1.2异常处理的两种方式

  • 使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver
  • 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器

1.4简单异常处理器SimpleMappingExceptionResolver

SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置

<bean
  class="org.springframework.web.servlet.handler"
  
</bean>

Spring的AOP简介

1.1 什么是AOP

AOP为Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
AOP是OOP的延续,是软件开发中的一个特定,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的
耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2 AOP的作用及其优势

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

1.3 AOP的底层实现

实际上,APO的底层是通过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 afterRuning(){
      System.out.println("后置增强。。。");
    }
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
  public static void main(String[] args) {
    final Target target = new Target();
    final Advice advice = new Advice();
    Proxy.newProxyInstance(
            target.getClass().getClassLoader(), //目标对象类加载器
            target.getClass().getInterface(), //目标对象相同的接口字节码对象数组
            new InvocationHandler() {
              //调用代理对象的任何方法,实质执行的都是invoke方法
              @Override
              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.6 cglib的动态代理

public class Target{
    public void save(){
      System.out.println("save running .....");
    }
    
}
public class Advice{
    public void before(){
      System.out.println("前置增强。。。");
    }
    public void afterRuning(){
      System.out.println("后置增强。。。");
    }
}

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
  public static void main(String[] args) {
    //目标对象
    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() {
      @Override
      public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //执行前置
        advice.before();
        //执行目标
        Object invoke = method.invoke(target, args);
        //执行后置
        advice.afterRunning();
        return invoke;
      }
    });

    //4. 创建代理对象
    Target proxy = (Target) enhancer.create();
  }
}

1.7 AOP相关概念

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

  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):☞那些被拦截到的点(方法)。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点
  • Pointcut(切入点):指我们要对哪些Joinpoint进行拦截的定义
  • Advice(通知/增强):所谓通知就是指拦截到Joinpoint之后要做的事情
  • Aspect(切面):是切入点和通知(引介)的结合
  • Weaving(织入):指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入

1.8 AOP开明确的事项

  1. 需要编写的内容
  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中由通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
  1. AOP技术实现的内容
    Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行
  2. AOP底层使用哪种代理方式
    在Spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式

2. 基于XML的AOP开发

2.1 入门

  1. 导入AOP相关坐标
  2. 创建目标接口和目标类(内部有切点)
  3. 创建切面类(内部有增强方法)
  4. 将目标类和切面类的对象创建权交给Spring
  5. 在applicationConetxt.xml中配置织入关系
  6. 测试代码

配置

<!--引入空间-->

<!--目标对象-->
<bean id="target" class="{path}.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="{path}.myAspect"></bean>
<!--配置织入,告诉spring框架,那些切点需要进行哪些增强-->
<aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
      <!--切面:切点+通知-->
      <aop:before method="before" pointcut="execution(public void {path}.Target.save())"></aop:before>
      <aop:around method="around" pointcut="execution(* com.example.aop.*.*(..))"></aop:around>
      <aop:after-throwing method="afterThrowing" pointcut="execution(* com.example.aop.*.*(..))"></aop:after-throwing>
      <aop:after method="afterThrowing" pointcut="execution(* com.example.aop.*.*(..))"></aop:after>
    </aop:aspect>
</aop:config>


import com.example.demo01.reflect.Autowired;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {

  @Autowired
  private TargetInterface target;
  
  @Test
  public void test1(){
      target.save();
  }
}
import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect {
  public void before() {
    System.out.println("前置增强...");
  }
    
  public void afterRunning(){
    System.out.println("后置增强...");
  }
  public Object around(ProceedingJoinPoint pjp) throws Throwable{
    System.out.println("环绕前增强。。。。");
    Object proceed = pjp.proceed();
    System.out.println("环绕后增强。。。。");
    return proceed;
  }
  public void afterThrowing(){
    System.out.println("异常抛出增强。。。。。。");
  }

  public void after(){
    System.out.println("最终增强。。。。。。");
  }
}

2.通知的类型

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

名称标签说明
前置通知<aop:before>用于配置前置通知。指定增强方法在切入点之前执行
后置通知<aop:after-running>用于配置后置通知。指定增强的方法在切入点之后执行
环绕通知<aop:around>用于配置环绕通知。指定增强方法在切入点之前和之后都执行
异常抛出通知<aop:throwing>用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知<aop:after>用于配置最终通知。无论增强方式执行是否有异常都会执行

3.切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式。
<aop:pointcut id="pointcut01" expression="execution(* com.example.aop.*.*(..))"

<aop:around method="around" pointcut-ref="pointcut01"

3.基于注解的AOP开发

3.1 快速入门

基于注解的AOP开发步骤:

  1. 创建目标接口和目标类(内部有切点)
  2. 创建切面类(内部有增强方法)
  3. 将目标类和切面类的对象创建权交给Spring
  4. 在切面类中使用注解配置织入关系
  5. 在配置文件中开启组件扫描和AOP的自动代理
  6. 测试

coding

import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component("myAspect")
@Aspect //标注当前的MyAspect是一个切面类
public class myAspect {

  @Before("execution(* com.example.aop.*.*(..))")
  public void before() {
    System.out.println("前置增强...");
  }

  public void afterRunning() {
    System.out.println("后置增强...");
  }

  public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕前增强。。。。");
    Object proceed = pjp.proceed();
    System.out.println("环绕后增强。。。。");
    return proceed;
  }

  public void afterThrowing() {
    System.out.println("异常抛出增强。。。。。。");
  }

  public void after() {
    System.out.println("最终增强。。。。。。");
  }
}

3.2 注解配置AOP详解

1.注解通知的类型

名称标签说明
前置通知@Before用于配置前置通知。指定增强方法在切入点之前执行
后置通知@AfterRunning用于配置后置通知。指定增强的方法在切入点之后执行
环绕通知@Around用于配置环绕通知。指定增强方法在切入点之前和之后都执行
异常抛出通知@AfterThrowing用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知@After用于配置最终通知。无论增强方式执行是否有异常都会执行

2.切点表达式的抽取

在切面内方法上使用@Pointcut注解定义切点表达式

import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component("myAspect")
@Aspect //标注当前的MyAspect是一个切面类
public class myAspect {

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

  @Pointcut("execution(* com.example.aop.*.*(..))")
  public void myPoint(){}
}

1.编程式事物控制相关对象

1.1 PlatformTransactionManager

PlatformTransactionManager接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法

方法说明
TransactionStatus getTransaction(TransactionDefination defination)获取事务的状态信息
void commit (TransactionStatus status)提交事务
void rollback(TransactionStatus status)回滚事务

注意:PlatformTransactionManager 是接口类型,不同的Dao层技术则有不同的实现类

1.2 TransactionDefinition

TransactionDefinition是事务的定义信息对象,里面有如下方法:

方法说明
int getIsolationLevel()获得事务的隔离级别
int getPropogationBehavior()获得事务的传播行为
int getTimeout()获得超时时间
boolean isReadOnly()是否只读
  1. 事务隔离级别
    设置隔离级别,可以解决事务并发产生的问题,如脏读,不可重复读,和续读。
  • ISOLATION_DEFAULT
  • ISOLATION_READ_UNCOMMITTED
  • ISOLATION_READ_COMMITTED
  • ISOLATION_REPEATABLE_READ
  • ISOLATION_SERIALIZABLE
  1. 事务的传播行为
说明
REQUIRED如果当前没有事务,就新建一个事务。如果存在一个事务,加入到 这个事务中,一般的选择(默认值)
SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW新建事务,如果当前在事务中,把当前事务挂起
NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER以非事务方式运行,如果当前存在事务,抛出异常
NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作
超时时间默认值是-1,没有超时限制。如果有,以秒为单位进行设置
是否只读建议查询时设置为只读

A调B,B看A有没有事务

1.3 TransactionStatus

Transaction接口提供的是事务具体的运行状态,方法介绍如下

方法说明
boolean hasSavepoint()是否存储回滚点
boolean isCompleted()事务是否完成
boolean isNewTransaction()是否是新事务
boolean isRollbackOnly()事务是否回滚

实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9tJaAg2Y-1632051628442)(C:\Users\Superclass\IdeaProjects\demo01\src\main\resources\SpingLearn\事务.png)]

<tx:advice id="txAdice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"></tx:method>
    <tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"></tx:method>
  </tx:attributes>
</tx:advice>

基于注解的声明事务控制


import com.example.demo01.reflect.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service("accountService")
@Transactional()
public class AccountServiceImpl implements AccountService {

  @Autowired
  private AccountDao accountDao;

  @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
  oublic

  void transfer(String outMan, String inMan, double money) {
    accountDao.out(outMan., money);
    accountDao.in(inMan, money);
  }
}

MyBatis

1.快速入门

开发步骤:

  1. 添加MyBatis坐标
  2. 创建user数据表
  3. 编写User实体类
  4. 编写映射文件UserMapper.xml
  5. 编写核心文件SqlMapConfig.xml
  6. 编写测试类

代码实现

mapping.xml

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

核心文件约束
sqlMapConfig

<?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="root"/>
      </dataSource>
    </environment>
  </environments>
  
  <!--加载映射文件-->
  <mappers>
    <mapper resource="com/example/mapper/UserMapper.xml"></mapper>
  </mappers>
  
</configuration>

测试

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 java.io.InputStream;
import java.util.List;

public class MyBatisTest {

  @Test
  public void test01() {
    //获取核心配置文件
    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();

  }
}

MyBatis的映射文件概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-50NdpkY0-1632051628444)(C:\Users\Superclass\IdeaProjects\demo01\src\main\resources\SpingLearn\MyBatis映射文件.png)]

MyBatis的基本操作

插入

mapping.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
  <!--插入操作-->
  <insert id="save" parameterType="com.example.domain.User">
    insert into user values(#{id},#{username},#{password})
  </insert>
  
  <!--查询操作-->
  <select id="findAll" resultType="com.example.domain.User">
    select * from user
  </select>
</mapper>
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 java.io.InputStream;
import java.util.List;

public class MyBatisTest {

  @Test
  public void test02() {
    
    //模拟user对象
    User user = new User();
    user.setUsername("张三");
    user.setPassword("123")
    //获取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    //获得session工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //获得session回话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    
    //执行操作 参数:namespace+id
    sqlSession.insert("userMapper.save",user);
    
    //mybatis默认事务不提交 要执行插入操作,就要提交数据
    sqlSession.commit();
    //打印数据
    System.out.println(userList);
    //释放资源
    sqlSession.close();

  }
}

插入操作注意问题

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

修改

<?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">
  
  <!--修改操作-->
  <update id="save" parameterType="com.example.domain.User">
    update user set username=#{username},password=#{password} where id=#{id}
  </update>
  
  <!--插入操作-->
  <insert id="save" parameterType="com.example.domain.User">
    insert into user values(#{id},#{username},#{password})
  </insert>
  
  <!--查询操作-->
  <select id="findAll" resultType="com.example.domain.User">
    select * from user
  </select>
</mapper>
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 java.io.InputStream;
import java.util.List;

public class MyBatisTest {

  @Test
  public void test03() {
    
    //模拟user对象
    User user = new User();
    user.setUsername("张三");
    user.setPassword("123")
    //获取核心配置文件
    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();
    //打印数据
    System.out.println(userList);
    //释放资源
    sqlSession.close();

  }
}

修改操作注意问题

  • 修改语句使用update标签
  • 修改操作使用的API是sqlSession.update("命名空间.id",实体对象);

删除

按id删除

<delete id="delete" parameterType="java.lang.Integer">
  delete from user where id=#{id}
</delete>
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 java.io.InputStream;
import java.util.List;

public class MyBatisTest {

  @Test
  public void test03() {
    //获取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    //获得session工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //获得session回话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    
    //执行操作 参数:namespace+id
    sqlSession.delete("userMapper.delete",7);
    
    //mybatis默认事务不提交 要执行插入操作,就要提交数据
    sqlSession.commit();
    //打印数据
    System.out.println(userList);
    //释放资源
    sqlSession.close();

  }
}

删除操作注意问题

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

Mybatis核心配置文件层级关系

  • configuration配置
    • properties 属性
    • settings 设置
    • typeAliases 类型别名
    • typeHandlers 类型处理器
    • objectFactory 对象工厂
    • plugins 插件
    • environments 环境
      • environment 环境变量
        • transactionManager 事务管理器
        • dataSource 数据源
    • databaseIdProvider 数据库厂商标识
    • mappers 映射器

常用配置解析

1. environment标签

数据库环境的配置,支持多环节配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HMvYtZkK-1632051628446)(C:\Users\Superclass\IdeaProjects\demo01\src\main\resources\SpingLearn\Mybatis配置.png)]
其中,事务管理器 transactionManager 类型有两种:

  • JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域
  • MANAGED:这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它的默认关闭行为
    其中,数据源 dataSource 类型有三种:
  • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接
  • POOLED:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来
  • JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用

2. mapper标签

该标签的作用是加载映射,加载方式有:

  • 使用相对于类路径的资源引用,例如:<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  • 使用完全资源定位符(URL),例如:<mapper url="file://var/mappers/AuthorMapper.xml"/>
  • 使用映射器接口实现类的完全限定类名,例如:<mapper class="org.mybatis.builder.AuthorMapper.xml"/>
  • 将包内的映射器接口实现全部注册为映射器,例如:<package name="org.mybatis.builder"/>

3. properties标签

sqlMapConfig.xml 可以通过properties 标签加载外部properties文件

4. typeAliases

自定义别名

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

MyBatis的相应API

1.SqlSession工厂构造器SqlSessionFactoryBuilder

常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流形式构建一个SqlSessionfactory对象

String resource = "prg/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

2.SqlSession工厂对象SqlSessionFactory

SqlSessionFactory有多个方法创建SqlSession实例,常用:

方法解释
openSession()会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库
openSession(boolean autoCommit)参数为是否自动提交,如果设置为true,那么不需要手动提交事务

3.SqlSession会话对象

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
int insert(String statement,Object parameter)
int update(String statement,Object parameter)
int delete(String statement,Object parameter)

操作事务的方法主要有:

void commit();
void rollback();

MyBatis的Dao层实现

1.1 传统开发方式

1. 编写UserDao接口

import java.io.IOException;

public interface UserDao {
  List<User> findAll() throws IOException;
}

1.2 代理开发方式

1. 规范

只需要编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法同上边Dao接口实现类方法
Mapper接口开发需要遵循一下规范:

  1. Mapper.xml文件中的namespace与mapper接口的全限定名相同
  2. Mapper接口方法名和Mapper.xml中定义的每一个statement的id相同
  3. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
  4. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

2. 编写UserMapper接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIgaSI7n-1632051628448)(C:\Users\Superclass\IdeaProjects\demo01\src\main\resources\SpingLearn\UserMapper.png)]

MyBatis文件深入

1.1 动态sql语句

1. 动态sql语句概述

Dynamic SQL
MyBatis 最强大的特性之一就是它的动态语句功能。如果您以前有使用 JDBC 或者类似框架的经历,您就会明白把 SQL 语句条件连接在一起是多么的痛苦,要确保不能忘记空格或者不要在 columns 列后面省略一个逗号等。动态语句能够完全解决掉这些痛苦。

尽管与动态 SQL 一起工作不是在开一个 party,但是 MyBatis 确实能通过在任何映射 SQL 语句中使用强大的动态 SQL 来改进这些状况。

动态 SQL 元素对于任何使用过 JSTL 或者类似于 XML 之类的文本处理器的人来说,都是非常熟悉的。在上一版本中,需要了解和学习非常多的元素,但在 MyBatis3 中有了许多的改进,现在只剩下差不多二分之一的元素。MyBatis 使用了基于强大的 OGNL 表达式来消除了大部分元素。

- if
- choose(when,otherwise)
- trim(where,set)
- foreach

2. if

实现:
UserMapper.xml

<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>

foreach

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

sql语句抽取

<sql id="selectUser">select * from user</sql>

<select id="findByCondition" parameterType="user" resultType="user">
  <include refid="selectUser"></include>
  <where>
    <foreach collection="list" open="id in(" close=")" item="id" separator=",">
      #{id}
    </foreach>
  </where>
</select>

MyBatis核心配置文件

1. typeHandler标签

无论是Mybatis在预处理语句(PrepareStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。

类型处理器Java类型JDBC类型
BooleanTypeHandlerjava.lang.Boolean, boolean数据库兼容的BOOLEAN
ByteTypeHandlerjava.lang.Byte,byte数据库兼容的NUMERICBYTE
ShortTypeHandlerjava.lang.Short,short数据库兼容的NUMERICSHORT INTEGER
IntegerTypeHandlerjava.lang.Integer,integer数据库兼容的NUMERICINTEFER
LongTypeHandlerjava.lang.Long,long数据库兼容的NUMERICLONG INTEGER

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

  1. 定义转换类继承类BaseTypeHandler
  2. 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换为java的Type类型的方法
  3. 在MyBatis核心配置文件中进行注册
  4. 测试转换是否正确
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class DateTypeHandler extends BaseTypeHandler<Date> {

  //java----->数据库
  @Override
  public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
    long time = date.getTime();
    preparedStatement.setLong(i, time);
  }

  //数据库中类型----->java
  //String参数 要转换的字段名称
  //ResultSet 查询出的结果集
  @Override
  public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
    //获得结果集中需要的数据(long----->Date)
    long aLong = resultSet.getLong(s);
    Date date = new Date(aLong);
    return date;
  }

  @Override
  public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
    long aLong = resultSet.getLong(s);
    Date date = new Date(aLong);
    return date;
    return null;
  }

  @Override
  public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
    long aLong = callableStatement.getLong(i);
    Date date = new Date(aLong);
    return date;
  }
}
<typeHandlers>
  <typeHandler handler="com.example.handler.DateTypeHandler"></typeHandler>
</typeHandlers>

2. plugins标签

MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页
开发步骤:

  1. 导入通用PageHelper的坐标
  2. 在mybatis核心配置文件中配置PageHepler插件
  3. 测试分页数据获取

PageHelper获取分页
PageHelper.startPage(1,3)//设置分页相关参数 当前页+每页显示显示的条数
PageInfo<User> pageInfo = new PageInfo<User>(userList)
pageInfo.getPageNum();//当前页
pageInfo.getPageSize();//每页显示条数
pageInfo.getTotal();//总条数
pageInfo.getPages();//总页数
pageInfo.getPrePage();//上一页
pageInfo.getNextPage();//下一页
pageInfo.isFirstPage();//是否是第一页

MyBatis多表操作

一对一

public class User{
    private int id;
    private String username;
    private String password;
}
import java.util.Date;

public class Order {
  private int id;
  private Date ordertime;
  private double total;
  private User user;
}

OrderMapper.xml

<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>-->
  <!--对象属性的封装
        property: 当前实体(Order)中的属性名称(private User user)
        javaType: 当前实体(Order)中的属性类型 (User)
  -->
  <association property="user" javaType="user">
    <id column="uid" property="id"></id>
      <result column="username" property="username"></result>
      <result column="password" property="password"></result>
  </association>
</resultMap>

<select id="findAll" resultMap="orderMap">
  SELECT *,o.id oid FROM orders o,USER u WHERE o.uid =u.id
</select>

一对多

public class User{
    private int id;
    private String username;
    private String password;
    private List<Order> orderList;
}

UserMapper.xml

<resultMap id="orderMap" type="order">
  <!--手动指定字段与实体属性的映射关系
        column:数据库的字段名称
        property:实体的属性名称
  -->
  <id column="uid" property="id"></id>
  <result column="uid" property=id"></result>
  <result column="username" property="username"></result>
  <result column="password" property="password"></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="orderMap">
  SELECT *,o.id oid FROM orders o,USER u WHERE o.uid =u.id
</select>

多对多

public class Role{
    private int id;
    private String roleName;
    private String roleDesc;
}
public class User{
    private int id;
    private String username;
    private String password;
    private List<Order> orderList;
    private List<Role> roleList;
}
<resultMap id="userRoleMap" type="user">

  <id column="userId" property="id"></id>
  <result column="uid" property=id"></result>
  <result column="username" property="username"></result>
  <result column="password" property="password"></result>
  <!--配置集合信息
    property:集合名称
    ofType: 当前集合中的数据类型 可用别名
  -->
  <collection property="roleList" ofType="role">
    <!--封装Role的数据-->
    <id column="roleId" property="id"></id>
    <result column="roleName" property="roleName"></result>
    <result column="roleDesc" property="roleDesc"></result>
  </collection> 
</resultMap>

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

MyBatis的注解开发

1.常用注解

注解效果
@Inseert新增
@Update更新
@Delete删除
@Select查询
@Result结果集封装
@Results可以与@Result一起使用,封装多个结果集
@One实现一对一结果集封装
@Many实现一对多结果集封装

2.Example

import org.apache.ibatis.annotations.Insert;

public interface UserMapper {

  @Insert("insert into user values(#{id},#{username},#{password}})")
  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();
  
}
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 java.io.InputStream;

public class MyBatisTest {
    private UserMapper mapper;
    
    @Before
    public void before() {
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapCongig.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("张三");
        user.setPassword("222");
        maper.save(user);
    }
    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(1);
        user.setUserName("kkk");
        user.setPassword("111");
        mapper.update(user);
    } 
    
    @Test
    public void delete(){
        mapper.delete(1);
    }
}

3.复杂查询

import org.apache.ibatis.annotations.Result;

public interface OrderMapper {

  @Select("select *,o.id oid from orders o,user u where o.uid=u.id")
  @Result({
          @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"),
  })
}

或者

import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;

public interface OrderMapper {

  @Select("select *,o.id oid from orders o,user u where o.uid=u.id")
  @Result({
          @Result(column = "oid", property = "id"),
          @Result(column = "ordertime", property = "ordertime"),
          @Result(column = "total", property = "total"),
          @Result(
                  property = "user", //要封装的属性名称
                  column = "uid", //根据那个字段去查询user表数据
                  javaType = User.class,//要封装的实体名称
                  //select属性,代表查询哪个接口的方法获得数据
                  one = @One(select = "com.example.mapper.UserMapper.findById")
          )
  })
}

一对多

import org.apache.ibatis.annotations.Select;

public interface OrderMapper{

  @Select("select * from orders where uid=#{uid}")
  public List<Order> findByUid(int uid);
  
}
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

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.example.mapper.OrderMapper.")
          )
  )
  public List<User> findUserAndOrderAll();
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值