Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)

前言:本篇文章是跟着 尚硅谷 视频教程学习写出来的写总结,目的在于帮助他人学习了解Spring,也为了巩固自己学习的知识,也方便往后查询。

目录

  • 一,Spring5 框架概述
    • 1、Spring 是轻量级的开源的 JavaEE 框架
    • 2、Spring 可以解决企业应用开发的复杂性
    • 3、Spring 有两个核心部分:IOC 和 Aop
    • 4、Spring 特点
  • 二,IOC
    • 1,IOC(概念和原理)
    • 2,IOC(BeanFactory 接口)
    • 3,IOC 操作 Bean 管理(概念)
    • 4,IOC 操作 Bean 管理(基于 xml 方式)
    • 5,IOC 操作 Bean 管理(基于注解方式)
  • 三,AOP
    • 1,AOP(概念)
    • 2,AOP(底层原理)
    • 3,AOP(JDK 动态代理)
    • 4,AOP(术语)
    • 5,AOP 操作(准备工作)
    • 6,AOP 操作(AspectJ 注解)
    • 7,AOP 操作(AspectJ 配置文件)
  • 四,JdbcTemplate 操作数据库
    • 1,概念和准备
    • 2,添加
    • 3,修改和删除
    • 4,查询返回某个值
    • 5,JdbcTemplate 操作数据库(查询返回对象)
    • 6,JdbcTemplate 操作数据库(查询返回集合)
    • 7,JdbcTemplate 操作数据库(批量操作)
  • 五,事务操作
    • 1,事务概念
    • 2,搭建事务操作环境
    • 3,Spring 事务管理介绍
    • 4,注解声明式事务管理
    • 5,声明式事务管理参数配置
    • 6,XML 声明式事务管理
    • 7,完全注解声明式事务管理
  • 六,Spring5 框架新功能
    • 1,新功能一
    • 2,Webflux

一,Spring5 框架概述

在实际开发中,通常服务器端在采用三层体系架构,分别为表示层(Web) 、业务逻辑层(Service)、持久层(Dao), Spring对每一层都提供了技术支持。

1、Spring 是轻量级的开源的 JavaEE 框架

2、Spring 可以解决企业应用开发的复杂性

3、Spring 有两个核心部分:IOC 和 Aop

(1)IOC:控制反转,把创建对象过程交给 Spring 进行管理

(2)Aop:面向切面,不修改源代码进行功能增强

4、Spring 特点

(1)方便解耦,简化开发
(2)Aop 编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低 API 开发难度
(7) 非侵入式设计

二,IOC

1,IOC(概念和原理)

1.1,什么是 IOC

  • 控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
  • 使用 IOC 目的:为了耦合度降低
  • 做入门案例就是 IOC 实现

1.2,IOC 底层原理

  • xml 解析、工厂模式、反射、

1.3,画图讲解 IOC 底层原理
Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第1张图片

2,IOC(BeanFactory 接口)

(1)IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂

(2)Spring 提供 IOC 容器实现两种方式:(两个接口)

a,BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用。

加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

b,ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。

加载配置文件时候就会把在配置文件对象进行创建

3,IOC 操作 Bean 管理(概念)

(1)什么是 Bean 管理

  • Bean 管理指的是两个操作
  • Spring 创建对象
  • Spirng 注入属性

(2)Bean 管理操作有两种方式

  • 基于 xml 配置文件方式实现
  • 基于注解方式实现

4,IOC 操作 Bean 管理(基于 xml 方式)

不常用,就暂不做笔记了

5,IOC 操作 Bean 管理(基于注解方式)

5.1,什么是注解

(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)

(2)使用注解,注解作用在类上面,方法上面,属性上面

(3)使用注解目的:简化 xml 配置

5.2,Spring 针对 Bean 管理中创建对象提供注解

(1)@Component

(2)@Service

(3)@Controller

(4)@Repository

上面四个注解功能是一样的,都可以用来创建 bean 实例

5.3、基于注解方式实现对象创建

第一步 引入依赖:

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第2张图片

第二步 开启组件扫描:

<!--
开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 扫描包上层目录 -->

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

第三步 创建类,在类上面添加创建对象注解

//在注解里面 value 属性值可以省略不写,
//默认值是类名称,首字母小写
//UserService--userService
@Component(value="userService") //
public class UserService {
    public void add() {
        System.out.
        println("service add.......");
    }
}

5.4、开启组件扫描细节配置

<!--
示例 1
use-default-filters="false"表示现在不使用默认 filter,
自己配filtercontext:include-filter ,设置扫描哪些内容 
-->

<context:component-scan base-package="com.atguigu"use-defaultfilters="false">
    <context:include-filter type ="annotation" expression = "org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--
示例 2
下面配置扫描包所有内容context:exclude-filter: 设置哪些内容不进行扫描 
-->
<context:component-scan base-package="com.atguigu">
    <context:excludefiltertype="annotation"expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

5.5、基于注解方式实现属性注入

(1)@Autowired:根据属性类型进行自动装配

  • 第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
  • 第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
    @Autowired
    private UserDao userDao;
    public void add() {
         System.out.println("service add.......");
        userDao.add();
    } 
}

(2)@Qualifier:根据名称进行注入

这个@Qualifier 注解的使用,和上面@Autowired 一起使用

//定义 dao 类型属性
    //不需要添加 set 方法
    //添加注入属性注解
    @Autowired //根据类型进行注入
    @Qualifier(value = "userDaoImpl1") //根据名称进行注入
    private UserDao userDao; 

(3)@Resource:可以根据类型注入,可以根据名称注入

 //@Resource //根据类型进行注入
    @Resource(name = "userDaoImpl1") //根据名称进行注入
    private UserDao userDao; 

(4)@Value:注入普通类型属性

@Value(value = "abc")    

private String name;

5.6、完全注解开发

(1)创建配置类,替代 xml 配置文件

@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}

(2)编写测试类

@Test
public void testService2() {
     //加载配置类
     ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
     UserService userService = context.getBean("userService", UserService.class);
     System.out.println(userService);
     userService.add();
}

三,AOP

1,AOP(概念)

1.1、什么是 AOP

(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

(3)使用登录例子说明 AOP

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第3张图片

2,AOP(底层原理)

2.1、AOP 底层使用动态代理

(1)有两种情况动态代理

第一种 有接口情况,使用 JDK 动态代理

  • 创建接口实现类代理对象,增强类的方法

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第4张图片

第二种 没有接口情况,使用 CGLIB 动态代理

  • 创建子类的代理对象,增强类的方法

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第5张图片

3,AOP(JDK 动态代理)

3.1、使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第6张图片

(1)调用 newProxyInstance 方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wn7W4gD6-1647091050832)(C:\Users\hhh\AppData\Local\YNote\data\qq04AFF178F8DB48674CD55641742EFFE2\b07c0810668e44b1a017e479e9cd45c5\clipboard.png)]

方法有三个参数:

  • 第一参数,类加载器
  • 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
  • 第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分

3.2、编写 JDK 动态代理代码

(1)创建接口,定义方法

public interface UserDao {
    public int add(int a,int b );
    public String update(String id);
}

(2)创建接口实现类,实现方法

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("实现了add方法");
        return a+b;
    }
    @Override
    public String update(String id) {
        System.out.println("实现了update方法");
        return id;
    }
}

(3)使用 Proxy 类创建接口代理对象

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import static java.lang.reflect.Proxy.newProxyInstance;

public class JDKProxy {
    public static void main(String[] args) {
        //创建接口实现类代理对象
        Class[] interfaces = {UserDao.class};
        // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces,new InvocationHandler() {
        // @Override
        // public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
        // return null;
        // }
        // });
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao) newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int add = dao.add(1, 2);
        System.out.println(add);
    }
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
    //1 把创建的是谁的代理对象,把谁传递过来
    //有参数构造传递
    private Object obj;
    public UserDaoProxy(Object obj){
        this.obj = obj;
    }
    //增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("方法之前执行...."+method.getName()+" :传递的参 数..."+ Arrays.toString(args));
        //被增强的方法执行
        Object res = method.invoke(obj, args);
        //方法之后
        System.out.println("方法之后执行...."+obj);
        return res;
    }
}

4,AOP(术语)

4.1、连接点

类里面哪些方法可以被增强,这些方法称为连接点。

4.2,切入点

实际被真正增强的方法,称为切入点。

4.3,通知(增强)

(1)实际增强的逻辑部分,称为通知(增强)。

(2)通知有多重类型

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知

4.4,切面

​ 是动作,把通知应用到切入点的过程

5,AOP 操作(准备工作)

5.1、Spring 框架

一般都是基于 AspectJ 实现 AOP 操作

(1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作

5.2、基于 AspectJ 实现 AOP 操作

(1)基于 xml 配置文件实现

(2)基于注解方式实现(建议使用)

5.3、在项目工程里面引入 AOP 相关依赖

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第7张图片

5.4、切入点表达式

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] 方法名称 )

**举例 1:**对 com.atguigu.dao.BookDao 类里面的 add 进行增强

execution(*  com.atguigu.dao.BookDao.add(..))

**举例 2:**对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强

execution(*  com.atguigu.dao.BookDao.*(..))

**举例 3:**对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强

execution(*  com.atguigu.dao.*.*(..))

6,AOP 操作(AspectJ 注解)

6.1、创建类,在类里面定义方法

import org.springframework.stereotype.Component;
//被增强的类
public class User {
    public void add(){
        System.out.println("add---------------");
    }
}

6.2、创建增强类(编写增强逻辑)

(1)在增强类里面,创建方法,让不同方法代表不同通知类型

//增强的类
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before-----------");
    }
}

6.3、进行通知的配置

(1)在 spring 配置文件中,开启注解扫描

<?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:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.vector.spring5.aopanno"></context:component-scan>

    <!-- 开启 Aspect 生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

(2)使用注解创建 User 和 UserProxy 对象

//被增强的类
@Component
public void User{
}
//增强的类
@Component
public void UserProxy{
}

(3)在增强类上面添加注解 @Aspect

//增强的类@Component
@Aspect //生成代理对象
public class UserProxy {
}

(4)在 spring 配置文件中开启生成代理对象

<!--开启 Aspect 生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

6.4、配置不同类型的通知

(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

//增强的类
@Component
@Aspect     //生成代理对象
public class UserProxy {
    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "execution(* com.vector.spring5.aopanno.User.add(..))")
    public void before(){
        System.out.println("before-----------");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.vector.spring5.aopanno.User.add())")
    public void afterReturning(){
        System.out.println("AfterReturning--------");
    }

    //最终通知
    @After(value = "execution(* com.vector.spring5.aopanno.User.add())")
    public void after(){
        System.out.println("After--------------");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.vector.spring5.aopanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }

    //环绕通知
    @Around(value = "execution(* com.vector.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

6.5、相同的切入点抽取

//相同切入点抽取
@Pointcut(value="execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value="pointdemo()")
public void before() {
    System.out.println("before.........");
}

6.6、有多个增强类多同一个方法进行增强,设置增强类优先级

(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高

@Component
@Aspect
@Order(1)
public class PersonProxy

6.7、完全使用注解开发

(1)创建配置类,不需要创建 xml 配置文件

@Configuration
@ComponentScan(basePackages={"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class ConfigAop {
}

7,AOP 操作(AspectJ 配置文件)

暂不做说明

四,JdbcTemplate 操作数据库

1,概念和准备

1.1、什么是 JdbcTemplate

(1)Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作

1.2、准备工作

(1)引入相关 jar 包
Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第8张图片

(2)在 spring 配置文件配置数据库连接池

<!-- 数据库连接池 -->
<bean id="dataSure" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    <property name="url" value="jdbc:mysql:///spring5?serverTimezone=UTC"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
</bean>

(3)配置 JdbcTemplate 对象,注入 DataSource

<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入 dataSource-->
    <property name="dataSource" ref="dataSure"></property>
</bean>

(4)创建 service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象

  • 配置文件
<!-- 组件扫描 -->
<context:component-scan base-package="com.vector"></context:component-scan>
  • Service
@Service
public class BookService {
    //注入 dao
    @Autowired
    private BookDao bookDao;
}
  • Dao
@Repository
public class BookDaoImpl implements BookDao{
    //注入 JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

2,添加

2.1、对应数据库创建实体类

public class Book {

    private String userId;
    private String userName;
    private String ustatus;

    @Override
    public String toString() {
        return "Book{" +
                "userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                ", ustatus='" + ustatus + '\'' +
                '}';
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public void setUstatus(String ustatus) {
        this.ustatus = ustatus;
    }
    public String getUserId() {
        return userId;
    }
    public String getUserName() {
        return userName;
    }
    public String getUstatus() {
        return ustatus;
    }
}

2.2、编写 service 和 dao

(1)在 dao 进行数据库添加操作

(2)调用 JdbcTemplate 对象里面 update 方法实现添加操作

update(String sql, Object... args)

一共有两个参数

  • 第一个参数:sql 语句
  • 第二个参数:可变参数,设置 sql 语句值
@Repository
public class BookDaoImpl implements BookDao{
    //注入 JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;


    //添加的方法
    @Override
    public void add(Book book){
        //1 创建 sql 语句
        String sql = "insert into book values(?,?,?)";

        //2 调用方法实现
        Object[] args = {book.getUserId(),book.getUserName(),book.getUstatus()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }
}

2.3、测试类

public class TestDemo1 {
    @Test
    public void TestBook(){

        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);

        //添加
        Book book = new Book();
        book.setUserId("1");
        book.setUserName("java");
        book.setUstatus("0");
        bookService.addBook(book);
    }
}

3,修改和删除

3.1、修改

@Override
public void updateBook(Book book) {
    String sql = "update book set username=?,ustatus=? where userid=?";
    Object[] args = {book.getUserName(), book.getUstatus(),book.getUserId()};
    int update = jdbcTemplate.update(sql, args);
    System.out.println(update);
}

3.2,删除

@Override
public void delete(String userId) {
    String sql = "delete from book where userid=?";
    int update = jdbcTemplate.update(sql, userId);
    System.out.println(update);
}

4,查询返回某个值

1、查询表里面有多少条记录,返回是某个值

2、使用 JdbcTemplate 实现查询返回某个值代码

queryFor0bject(String sql,Class<T> requiredType)

有两个参数

  • 第一个参数:sql 语句
  • 第二个参数:返回类型 Class

查询表记录数

@Override
public int selectCount(Book book) {
    String sql = "select count(*) from book";
    Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
    return count;
}

5,JdbcTemplate 操作数据库(查询返回对象)

5.1、场景:查询图书详情

5.2、JdbcTemplate 实现查询返回对象

 queryForObject(String sql,RowMapper<T> rowMapper,0bject... args)

有三个参数

  • 第一个参数:sql 语句
  • 第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成 数据封装
  • 第三个参数:sql 语句值

查询返回对象

@Override
public Book findBookInfo(String userId) {
    String sql = "SELECT * FROM book WHERE userid=?";
    //调用方法
    Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), userId);
    return book;
}

6,JdbcTemplate 操作数据库(查询返回集合)

6.1、场景:查询图书列表分页…

6.2、调用 JdbcTemplate 方法实现查询返回集合

 query(String sql,RowMapper<T> rowMapper,0bject.. . args)

有三个参数

  • 第一个参数:sql 语句
  • 第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
  • 第三个参数:sql 语句值

查询返回集合

@Override
public List<Book> findAllBook() {
    String sql = "select * from book";
    //调用方法
    List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
    return bookList;
}

7,JdbcTemplate 操作数据库(批量操作)

7.1、批量操作:操作表里面多条记录

7.2、JdbcTemplate 实现批量添加操作

batchUpdate(String sql,List<Object[> batchArgs)

有两个参数

  • 第一个参数:sql 语句
  • 第二个参数:List 集合,添加多条记录数据

批量添加

@Override
public void batchAddBook(List<Object[]> batchArgs) {
    String sql = "insert into book values(?,?,?)";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

测试类

//批量添加
        List<Object[]> batchArgs = new ArrayList<Object[]>();
        Object[] o1 = {"3","java","a"};
        Object[] o2 = {"4","c++","b"};
        Object[] o3 = {"5","MySQL","c"};
        batchArgs.add(o1);
        batchArgs.add(o2);
        batchArgs.add(o3);
        //调用批量添加
        bookService.batchAdd(batchArgs);

7.3、JdbcTemplate 实现批量修改操作

批量修改

@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
    String sql = "update book set username=?,ustatus=? where userid=?";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

测试类

//批量修改
List<Object[]> batchArgs = new ArrayList<Object[]>();
Object[] o1 = {"java11","a11","3"};
Object[] o2 = {"c++22","b22","4"};
Object[] o3 = {"MySQL22","c22","5"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//调用批量添加
bookService.batchUpdate(batchArgs);

7.4、JdbcTemplate 实现批量删除操作

批量删除

@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
    String sql = "delete from book where userid=?";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

测试类

//批量删除
List<Object[]> batchArgs = new ArrayList<Object[]>();
Object[] o1 = {"3"};
Object[] o2 = {"4"};
Object[] o3 = {"5"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//调用批量添加
bookService.batchDelete(batchArgs);

五,事务操作

1,事务概念

1.1、什么事务

(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败

(2)典型场景:银行转账

  • *lucy 转账 100 元 给 mary
  • *lucy 少 100,mary 多 100

1.2、事务四个特性(ACID)

(1)原子性(Atomicity):

事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,只允许出现两种状态之一,要么都成功,要么都失败

任何一项操作都会导致整个事务的失败,同时其它已经被执行的操作都将被撤销并回滚,只有所有的操作全部成功,整个事务才算是成功完成

(2)一致性:(Consistency):

事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处以一致性状态。

比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱

(3)隔离性(Isolation):

事务的隔离性是指在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其它事务干扰。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。

一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的

(4)持久性(Duration):

事务的持久性是指事务一旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态

在事物进行过程中,未结束之前,DML语句是不会更改底层数据,只是将历史操作记录一下,在内存中完成记录。只有在事物结束的时候,而且是成功的结束的时候,才会修改底层硬盘文件中的数据

2,搭建事务操作环境

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第9张图片

2.1、创建数据库表,添加记录

2.2、创建 service,搭建 dao,完成对象创建和注入关系

(1)service 注入 dao,在 dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource

@Service
public class UserService {
    //注入 dao
    @Autowired
    private UserDao userDao;
}
@Repository
public class UserDaoImpl implements UserDao {
    @Autowired 
    private JdbcTemplate jdbcTemplate;
}

2.3、在 dao 创建两个方法:多钱和少钱的方法,在 service 创建方法(转账的方法)

@Repository
public class UserDaoImpl implements UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    //lucy 转账 100 给 mary
    //少钱
    @Override
    public void reduceMoney() {
        String sql="update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql,100,"lucy");
    }
    //多钱
    @Override
    public void addMoney() {
        String sql="update t_account set money=money+? where username=?";
        jdbcTemplate.update(sql,100,"mary");
    }
}
@Service
public class UserService {
    //注入 dao
    @Autowired
    private UserDao userDao;
    //转账的方法
    public void accountMoney() {
        //lucy 少 100
        userDao.reduceMoney();
        //mary 多 100
        userDao.addMoney();
    }
}

2.4、上面代码,如果正常执行没有问题的,但是如果代码执行过程中出现异常,有问题

//转账的方法
public void accountMoney(){
    //lucy少100
    userDao.reducMoney();
    
    //模拟异常
    int i = 10/0;
    
    //mary多100
    userDao.addMoney();
}

(1)上面问题如何解决呢?

使用事务进行解决

(2)事务操作过程

public void accountMoney(){
    try{
        //1,开启事务

        //2,进行事务操作

        userDao.addMoney();
        //模拟异常
        //int a = 10/0;
        userDao.reduceMoney();

        //3,没有发生异常,提交事务
    }catch(Exception e){
        //4,出现异常,事务回滚
    }
}

3,Spring 事务管理介绍

3.1、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)

3.2、在 Spring 进行事务管理操作

(1)有两种方式:编程式事务管理和声明式事务管理(使用)

3.3、声明式事务管理

(1)基于注解方式(建议使用)

(2)基于 xml 配置文件方式

3.4、在 Spring 进行声明式事务管理,底层使用 AOP 原理

3.5、Spring 事务管理 API

(1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第10张图片

4,注解声明式事务管理

4.1、在 spring 配置文件配置事务管理器

<!--创建事务管理器-->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.
DataSourceTransactionManager">
<!--注入数据源-->
    <property name="dataSource"ref="dataSource"></property>
</bean>

4.2、在 spring 配置文件,开启事务注解

(1)在 spring 配置文件引入名称空间 tx

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.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">

(2)开启事务注解

<!--开启事务注解-->
<tx:annotation-driven transactionmanager="transactionManager">
</tx:annotation-driven>

4.3、在 service 类上面(或者 service 类里面方法上面)添加事务注解

(1)@Transactional,这个注解添加到类上面,也可以添加方法上面

(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务

(3)如果把这个注解添加方法上面,为这个方法添加事务

@Service
@Transactional
public class UserService {
}

5,声明式事务管理参数配置

5.1、在 service 类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第11张图片

5.2、propagation:事务传播行为

(1)多事务方法直接进行调用,这个过程中事务 是如何进行管理的

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第12张图片

事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第13张图片

事例:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class UserService {
    }

5.3、ioslation:事务隔离级别

(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题

(2)一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。

​ 不同的隔离级别可以解决不同的问题,SQL的标准事务隔离级别包括:

读未提交(read uncommitted): 一个事务还没有提交时,它做的变更就能被别的事务看到。

读提交(read committed): 一个事物提交之后,它做的变更才会被其他事务看到。

可重复读(repeatable read): 一个事物执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交变更对其他事务也是不可见的。

串行化(serializable): 对于同一行记录,写会加“写锁”,读会加“读锁”,当出现锁冲突时,后访问的事务需要等前一个事务执行完成,才能继续执行。

(3)可能导致 读问题 有三个:脏读、不可重复读、虚(幻)读

  • 脏读:一个未提交事务读取到另一个未提交事务的数据

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第14张图片

  • **不可重复读:一个未提交事务读取到另一提交事务修改数据(**一个事务两次读同一行数据,可是这两次读到的数据不一样。)

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第15张图片

  • 虚读:一个未提交事务读取到另一提交事务添加数据

    (一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。)

(6)解决:通过设置事务隔离级别,解决读问题

脏读 不可重复读 虚读
READ UNCOMMITTED(读未提交)
READ COMMITTED(读已提交)
REPEATABLE READ(可重复读)
SERIALIZABLEJ(串行化)

事例:

@Service
@Transactional(propagation = Propagation.REQUIRED , isolation = Isolation.REPEATABLE_READ)
public class UserService {
    }

5.4、timeout:超时时间

(1)事务需要在一定时间内进行提交,如果不提交进行回滚

(2)默认值是-1 ,设置时间是以秒为单位进行计算的。

5.5、readOnly:是否只读

(1)读:查询操作,写:添加修改删除操作

(2)readOnly 默认值 false:表示可以查询,可以添加修改删除操作

(3)设置 readOnly 值是 true:设置成 true 之后,只能查询

5.6、rollbackFor:回滚

(1)设置出现哪些异常进行事务回滚

5.7、noRollbackFor:不回滚

(1)设置出现哪些异常不进行事务回滚

6,XML 声明式事务管理

1、在 spring 配置文件中进行配置第一步 配置事务管理器第二步 配置通知第三步 配置切入点和切面

<!--1 创建事务管理器-->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource"ref="dataSource"></property>
</bean>

<!--2 配置通知-->
<tx:advice id="txadvice">
    <!--配置事务参数-->
    <tx:attributes>
        <!--指定哪种规则的方法上面添加事务-->
        <tx:method name="accountMoney"propagation="REQUIRED"/>
        <!--<tx:method name="account*"/>-->
    </tx:attributes>
</tx:advice>

<!--3 配置切入点和切面-->
<aop:config>
    <!----配置切入点-->
    <aop:pointcut id="pt"expression="execution(*com.atguigu.spring5.service.UserService.*(..))"/>
    <!--配置切面-->
    <aop:advisor advice-ref="txadvice"pointcut-ref="pt"/>
</aop:config>

7,完全注解声明式事务管理

1、创建配置类,使用配置类替代 xml 配置文件

@Configuration //配置类
@ComponentScan(basePackages = "com.atguigu") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
     //创建数据库连接池
     @Bean
     public DruidDataSource getDruidDataSource() {
         DruidDataSource dataSource = new DruidDataSource();
         dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///spring5?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
     //创建 JdbcTemplate 对象
     @Bean
     public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
         //到 ioc 容器中根据类型找到 dataSource
         JdbcTemplate jdbcTemplate = new JdbcTemplate();
         //注入 dataSource
         jdbcTemplate.setDataSource(dataSource);
         return jdbcTemplate;
     }
     //创建事务管理器
     @Bean
     public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
         DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
         transactionManager.setDataSource(dataSource);
         return transactionManager;
     } 
}

六,Spring5 框架新功能

1,新功能一

1.1、整个 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,许多不建议使用的类和方法在代码库中删除

1.2、Spring 5.0 框架自带了通用的日志封装

(1)Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2

(2)Spring5 框架整合 Log4j2第一步 引入 jar 包

Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第16张图片

第二步 创建 log4j2.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置,当设置成 trace 时,可以看到 log4j2 内部各种详细输出--> 
<configuration status="INFO">
    <!--先定义所有的 appender-->
    <appenders>
        <!--输出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定义 logger,只有定义 logger 并引入的 appender,appender 才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定 Logger,则会使用 root 作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

1.3、Spring5 框架核心容器支持@Nullable 注解

(1)@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空

(2)注解用在方法上面,方法返回值可以为空

@Nullable
String getId() ;

(3)注解使用在方法参数里面,方法参数可以为空
Spring学习笔记(IOC,AOP,JdbcTemplate,事务,新功能等)_第17张图片

(4)注解使用在属性上面,属性值可以为空

@Nullable
private String bookName;

1.4、Spring5 核心容器支持函数式风格 GenericApplicationContext

//函数式风格创建对象,交给 spring 进行管理
@Test
public void testGenericApplicationContext() {
    //1 创建 GenericApplicationContext 对象
    GenericApplicationContext context = new GenericApplicationContext();
    //2 调用 context 的方法对象注册
    context.refresh();
    context.registerBean("user1",User.class,() -> new User());
    //3 获取在 spring 注册的对象
    // User user = (User)context.getBean("com.atguigu.spring5.test.User");
    User user = (User)context.getBean("user1");
    System.out.println(user);
}

1.5、Spring5 支持整合 JUnit5

(1)整合 JUnit4第一步 引入 Spring 相关针对测试依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qBpTnnJ2-1647091050836)(C:\Users\hhh\AppData\Local\YNote\data\qq04AFF178F8DB48674CD55641742EFFE2\cf32ad0daef5429dab55e12d263a79c7\clipboard.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HufvNqEC-1647091050837)(C:\Users\hhh\AppData\Local\YNote\data\qq04AFF178F8DB48674CD55641742EFFE2\4ea6579821cd4f89a1b60ef0a138b131\clipboard.png)]

第二步 创建测试类,使用注解方式完成

@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架
@ContextConfiguration("classpath:bean1.xml") //加载配置文件
public class JTest4 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    }
} 

(2)Spring5 整合 JUnit5第一步 引入 JUnit5 的 jar 包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YDAZSxJQ-1647091050837)(C:\Users\hhh\AppData\Local\YNote\data\qq04AFF178F8DB48674CD55641742EFFE2\b4041515cf0949599b081b56a0edbf12\clipboard.png)]

第二步 创建测试类,使用注解完成

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
public class JTest5 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    } 
}

(3)使用一个复合注解替代上面两个注解完成整合

@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5 {
    @Autowired
    private UserService userService;
    @Test
    public void test1() {
        userService.accountMoney();
    } 
}

2,Webflux

有很多涉及的知识我还没学习,所以暂不做总结。

你可能感兴趣的:(Java学习,java,spring,java-ee)