SSM 整合

零、配置

1、导入依赖

junit,数据库驱动,连接池,servlet,jsp,mybatis,spring,mybattis-spring…

2、IDEA连接数据库

3、Spring配置文件

image-20260412211429553

4、Mybatis配置文件

image-20260412211535889

5、配置数据库

image-20260412211844548

一、MyBatis

DAO层

作用:直接操作数据库

使用MyBatis,需要的文件有.java,.xml,在程序运行时,MyBatis会自动生成动态代理实现类取操作数据库。

.java 负责写接口定义

.xml 映射文件,负责写SQL语句

image-20260412210316039

注意:需要在mybatis-config.xml中注册Mapper接口文件,否则MyBatis找不到

image-20260412211717211

Serive层

作用:实现业务功能

Service层调用DAO层

image-20260412211049308

二、Spring

关联spring和DAO层

image-20260413150641995

image-20260413150722253

关联spring和service层

image-20260413151340001

三、SpringMVC

与web相连

image-20260413161711723

image-20260413161744999

SpringMVC

image-20260413163633671

Tips

lombok 偷懒

注解:

@Data:自动生成所有属性的get、set、toString、equals、hashCode方法

@AllArgsConstructor:生成全参构造

@NoArgsConstructor:生成无参构造

@SELECT

Spring

一、Bean的获取方式

1、在spring配置文件中直接配置类的全路径

spring会自动调用该类的无餐构造方法来实例化Bean

image-20260413194959456

image-20260413195021515

2、简单工厂模式

一个工厂对应所有商品。

在Spring配置文件中告诉Spring,调用哪个类的哪个方法来获取Bean

spring.xml:

image-20260413195236189

简单工厂模式的方法是静态方法

image-20260413195344542

3、factory-bean 工厂方法模式

和方法2的区别:

  • 一个工厂只能对应一个商品,获取商品的方法是实例化商品
  • factory本身也是一个bean,也需要被spring进行管理。

image-20260413200152440

工厂方法模式中的具体工厂角色的方法是实例方法

image-20260413200248811

4、FactoryBean接口

编写的类去实现FactoryBean接口

image-20260413202217917

image-20260413202121358

其中isSingleton返回的内容是指这个类对应的对象是否为单例对象,如果则返回true

在程序运行过程中,只会有这一个对象,节省内存。

BeanFactory和FactoryBean的区别?

BeanFactory是Been工厂,负责创建Bean对象

FactoryBean本身是一个Bean,用于辅助Spring实例化其他普通的Bean对象

实战-注入Date

image-20260413204634591

方法一、把日期类型当做简单类型

直接写在property中

方法二、当做非简单类型,只能获取系统当前时间

image-20260413203509627

方法三、通过工厂Bean:DateFactoryBean来返回普通Bean:java.util.Date

image-20260413204721992

image-20260413204506812

二、Bean的生命周期

1、五步

  1. 实例化Bean(调用无参构造)
  2. 给Bean属性赋值(调用setXxx方法)(set方法的名称需要与property中的属性名称一致)
  3. 初始化Bean(调用Bean的init方法。init需要自己写,自己配)
  4. 使用Bean
  5. 销毁Bean(调用Bean的destroy方法。destroy需要自己写,自己配)

image-20260414110838044

2、七步

  1. 实例化Bean
  2. Bean属性赋值
  3. 执行“Bean后处理器”的before方法
  4. 初始化Bean
  5. 执行“Bean后处理器”的after方法
  6. 使用Bean
  7. 销毁Bean

image-20260414112216673

image-20260414112503027

注意:Bean后处理器作用于整个.xml文件中的所有bean,每个bean的生命周期都会执行这两个处理器。

3、十步

  1. 实例化Bean
  2. Bean属性赋值
  3. 检查Bean是否实现了Aware相关接口,并调用接口方法(传递一些数据,让你更加方便使用)
  4. 执行“Bean后处理器”的before方法
  5. 检查Bean是否实现了InitializingBean接口,并调用接口方法
  6. 初始化Bean
  7. 执行“Bean后处理器”的after方法
  8. 使用Bean
  9. 检查Bean是否实现了DisposableBean接口,并调用接口方法(保证资源释放,用在中间件的开发)
  10. 销毁Bean

添加这三个点位的特点都是在检查这个Bean是否实现了某些接口,如果实现了这些接口,则Spring会调用这个接口中的方法

Bean的作用域不同,管理方式不同

Spring只对singleton的Bean进行完整生命周期的管理,如果是prototype作用域的Bean,Spring容器只负责将该Bean初始化完毕。等客户端程序一旦获取这个Bean后,Spring就不再管理它的生命周期了。

(第9步开始就不管了,也就是不管销毁的流程)

image-20260414114607386

把自己new的对象纳入Spring容器管理

image-20260414120300965

三、Bean的循环依赖

3.1 singleton和setter模式(正常)

image-20260414163304913

image-20260414163555064

分析:在这种模式下,Spring对Bean的管理分为清晰的两个阶段

  1. Spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行曝光【不等属性赋值就曝光】
  2. Bean曝光之后,再进行属性的赋值(调用set方法)

流程举例:

  1. 实例化husband
  2. 曝光husband,通知别人可以引用husband了(因为是单例的,反正就这一个,要用你就先拿着吧)
  3. husband运行set方法,发现需要wife,并且wife不存在
  4. 实例化wife
  5. 曝光wife
  6. wife运行set方法,需要使用husband(已曝光),直接引用即可【wife 完成全流程】
  7. 回到husband,运行set方法,引用wife【husband 完成全流程】

3.2 prototype和setter模式(异常)

prototype导致每次创建的bean都是新的,husband使用set方法后发现要创建wife,wife使用set方法后发现要创建husband,会导致这样循环下去。

3.3 一个singleton一个prototype(正常)

单例的Bean在new ClassPathXmlApplicationContext("spring.xml");的时候就创建了。

3.4 构造注入模式(异常)

即在构造方法中进行赋值。

husband在构造方法中发现要创建wife(此时都没构造完成,无法曝光),wife在构造方法中发现要创建husband(也无法曝光),导致循环依赖。

final. 源码分析

Bean都是单例的情况下,可以把所有单例的Bean实例化出来,放到一个集合中(可以称之为缓存),当所有单例Bean全部实例化完成之后,再慢慢调用setter方法给属性赋值。解决了循环依赖的问题。

三级缓存

image-20260414193453109

存储的内容

一级缓存:完整的单例Bean对象,也就是说这个缓存中的Bean对象的属性都已经赋值了。是一个完整的Bean对象。

二级缓存:早期的单例Bean对象,这个缓存中的单例Bean对象的属性没有赋值。只是一个早期的实例对象。

三级缓存:单例工厂对象。这个里面存储了大量的“工厂对象”,每一个单例Bean对象都会对应一个单例工厂对象。

流程:先去一级缓存拿Bean对象,拿不到就去二级缓存,也拿不到就去三级缓存,通过工厂对象获取Bean对象,然后把它放到二级缓存中。

四、回顾反射机制

调用一个方法,包含的四要素

  1. 对象
  2. 方法
  3. 传参
  4. 返回值

举例:

1、通过反射运行函数

image-20260414200019772

2、通过反射对属性进行赋值

image-20260414201040935

五、Spring IoC 注解式开发

IoC(Inversion of Contro)控制反转:“创建和管理对象”的权力,从“程序员/代码本身”转移(反转)给了“Spring 容器”。

注解式开发的作用:简化XML的配置

5.1 回顾-自定义注解

image-20260414204636993

5.2 回顾-反射获取注解

image-20260414205151930

5.3 回顾-组件扫描

只知道一个包的名字,扫描这个包下所有的类。当这个类上有@xxx注解时,实例化该对象,放到Map集合中。

070-Spring IoC注解之组件扫描原理_哔哩哔哩_bilibili

注解的作用就是让程序通过反射获取注解中的内容,有xx注解就做某某事情,没有就不做。因此,省去了自己编写某些常用程序的时间。

5.4 声明Bean的注解

  • @Component(老大,其他三个其实是Component的别名,为了增强程序的可读性,引入下面三个注解)
  • @Controller
  • @Service
  • @Repository (仓库 -> DAO层)

5.5 Spring注解的使用

  1. 加入aop的依赖
  2. 在配置文件中添加context命名空间image-20260414212100943
  3. 在配置文件中指定扫描的包image-20260414212242228
  4. 在Bean类上使用注解image-20260415120953973

如果只写Bean的注解,把整个value属性全部省略了,bean的默认名称是:类名(首字母变小写)

5.6 解决多个包扫描的问题

方法一:在指定扫描包的时候,写多个包,用逗号隔开

image-20260414213112871

方法二:指定多个包共同的 父包,牺牲一部分效率

image-20260414213211320

5.7 选择性实例化Bean

由于特殊业务需要,只要求Controller注解进行实例化,如何完成?

方法一

use-default-filters="false" ,并在其中添加include-filter,来指定要实例化的注解

image-20260415101258552

方法二

use-default-filters="true",并在其中添加exclude-filter,来指定失效的注解

image-20260415101411330

5.8 负责注入的注解

给Bean的属性赋值

  • @Value 注入简单类型,代替Bean配置代码中的property行。(可以用在 属性方法构造方法 上)
  • @Autowired
  • @Qualifier
  • @Resource

**5.8.1 @Value **

注入简单类型

有这个注解以后,可以不提供set方法。

属性:

image-20260415102854849

方法:

image-20260415102657918

构造方法:

image-20260415102818500

5.8.2 @Autowired和@Qualifier

注入非简单类型

只用@Autowired,默认根据类型装配【byType】想要根据名字进行装配需要配合@qualifier

使用的时候不需要指定属性的值,直接根据类型进行装配。

image-20260415105304033

(如果一个类的构造方法只有一个,并且构造方法上的参数和属性能够对应上。@Autowired注解可以省略)

5.8.3 @Resource

非简单类型的注入。

与@Autowired的区别:

  • @Resource是JDK扩展包中的,是标准规范更加通用。@Autowired是Spring框架自己的。
  • @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的时候会自动通过byType装配。
  • @AutoWired默认根据类型装配byType,如果需要根据名称装配,需要配合@Qualifier
  • @Resource用在 属性set方法
  • @Autowired用在 属性set方法构造方法构造方法参数

image-20260415111358787

5.8.4 全注解开发

不使用spring配置文件,使用一个配置类来替代配置文件。

配置类:

image-20260417171051630

image-20260417171135047

六、GoF之代理模式

6.1 作用

  1. 一个对象需要受到保护的时候
  2. 需要给某个对象进行功能增强的时候
  3. A对象和B对象无法直接交互时,也可以使用代理模式来解决(找个中间人)

代理模式中有三大角色

  1. 目标对象(演员)
  2. 代理对象(替身)
  3. 目标对象和代理对象的公共接口(两个对象应该具有相同的行为动作->不想让观众看出来是替身。即客户端无法察觉出是代理对象。)

6.2 静态代理

如果需要对所有业务接口中的每一个业务方法统计耗时,应该怎么办?

  • 方法一:硬编码。在每个方法中添加耗时统计的代码
    • 缺点1:违背OCP开闭原则
    • 缺点2:代码没有得到复用
  • 方法二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。(super.xxx方法前后添加统计耗时的代码)
    • 缺点1:采用了继承关系,耦合度很高
    • 缺点2:代码依然没有得到服用
  • 方法三:静态代理
    • 优点1:解决了OCP问题
    • 优点2:降低了耦合度,是has a,而不是is a
    • 缺点:类爆炸。假设系统有1000个接口,每个接口都需要对应的代理类,这样类会急剧膨胀。不好维护。
    • image-20260416191430087
    • 运行流程:创建目标对象、创建代理对象、调用代理对象的代理方法
    • 为什么传入的参数类型是接口而不是某一类
      • 公共接口,降低耦合度。只要实现了这一个接口的类都可以代理(向上转型,在使用时会调用对应对象实现的方法)。
      • 【补充】类的继承也可以向上转型,比如子类赋值给父类。但是调用方法的时候还是使用子类的方法。(必须得是父类也有的方法,否则编译无法通过。)【编译看左边,运行看右边。】舍弃掉实现类/子类的特殊细节,把他们统一当做高层级的抽象类型来看待。
  • 方法四:动态代理
    • 添加了字节码生成技术。可以在内存中动态生成一个class字节码,这个字节码就是代理类。(不需要自己写代理类的代码了)

6.3 动态代理

  • JDK动态代理技术:只能代理接口
  • CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的(目标类不能用final修饰)。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
  • Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态”AOP”框架。

6.3.1 JDK 动态代理

image-20260416193044845

Proxy.newProxyInstance()做了两件事:

  1. 在内存中动态生成了一个代理类的字节码class文件
  2. new了一个对象。通过动态生成的class文件实例化了代理对象

调用处理器中(需要实现 InvocationHandler 接口),需要写的内容:

  1. 一个对象(用于接收目标对象,用Object去接收比较好【向上转型,降低耦合度】)
  2. 构造函数(将目标对象赋值给变量)
  3. 实现invoke方法(其中用method.invoke调用目标对象的方法,前后写上增强代码)image-20260416195805808

带返回值的目标函数,如何让代理方法返回?

在目标函数返回值给invoke后,invoke方法必须继续返回(相同的返回值返回了两层)

image-20260416200732036

6.3.2 CGLIB 动态代理

image-20260416202724325

image-20260416202756408

七、面向切面编程 AOP

7.1 切面

切面:在业务流程中与具体业务无关的通用代码提取出来,形成横向的切面(交叉业务),业务是纵向的,把切面应用于不同业务的过程,叫做面向切面编程。

优点:

  1. 代码复用性强
  2. 代码易维护,只需要修改一遍
  3. 开发者可以更加专注于业务逻辑

image-20260416203740826

Spring的AOP使用JDK+CGLIB动态代理(这两个动态代理就是AOP的具体实现

  • 代理接口,默认使用JDK。
  • 代理某个类,使用CGLIB。

7.2 七大术语

1、连接点 joinpoint

在程序的整个执行流程中,可以织入切面的位置。也就是所有的方法(候选)

2、切点 pointcut

在程序执行流程中,真正织入切面的方法

切点是一组特定的连接点的集合。

3、通知 advice

又叫增强,就是你具体要织入的代码

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

4、切面 Aspect

切点+通知

5、织入 Weaving

把通知应用到目标对象的过程

6、代理对象 Proxy

一个目标对象被织入通知后产生的新对象

7、目标对象 Target

被织入通知的对象

image-20260416205516374

注意:这张图上的连接点画错了,应该是所有方法,而不是间隙。

7.3 切点表达式

execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])

访问控制权限修饰符

  • 可选项。
  • 没写,就是4个权限都包括。
  • 写public就表示只包括公开的方法。

返回值类型

  • 必填项。
  • * 表示返回值类型任意。

全限定类名

  • 可选项。
  • 两个点“..”代表当前包以及子包下的所有类。
  • 省略时表示所有的类。

方法名

  • 必填项。
  • *表示所有方法。
  • set*表示所有的set方法。

形式参数列表

  • 必填项

  • () 表示没有参数的方法

  • (..) 参数类型和个数随意的方法

  • (*) 只有一个参数的方法

  • (*, String) 第一个参数类型随意,第二个参数是String的。

异常

  • 可选项。
  • 省略时表示任意异常类型。

八、Spring AOP的实现

三种方式:

  1. Spring框架结合AspectJ框架实现的AOP,基于注解方式
  2. Spring框架结合AspectJ框架实现的AOP,基于XML
  3. Spring框架自己实现的AOP,基于XML配置方式(没用)

8.1 准备工作

引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--spring context依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring aop依赖(引入context就自带aop了,不需要写了)-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0-M2</version>
</dependency>

添加context命名空间和aop命名空间

1
2
3
4
5
6
7
8
9
10
<?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">

</beans>

8.2 基于注解的实现步骤

1、定义目标类和目标方法(并用注解在spring中注册)

1
2
3
4
5
6
7
8
9
10
package com.powernode.spring6.service;

// 目标类
@Service
public class OrderService {
// 目标方法
public void generate(){
System.out.println("订单已生成!");
}
}

2、定义切面类(并注册)

1
2
3
4
5
6
7
8
9
package com.powernode.spring6.service;

import org.aspectj.lang.annotation.Aspect;

// 切面类
@Component
@Aspect
public class MyAspect {
}

3、在Spring配置文件中添加组件扫描

1
<context:component-scan base-package="com.powernode.spring6.service"/>

4、在切面类中添加通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.powernode.spring6.service;

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

// 切面类
@Aspect
@Component
public class MyAspect {
// 这就是需要增强的代码(通知)
public void advice(){
System.out.println("我是一个通知");
}
}

5、在通知上添加切点表达式(筛选要增强的切点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.powernode.spring6.service;

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

// 切面类
@Aspect
@Component
public class MyAspect {

// 切点表达式
@Before("execution(* com.powernode.spring6.service.OrderService.*(..))")
// 这就是需要增强的代码(通知)
public void advice(){
System.out.println("我是一个通知");
}
}

@Before表示这是一个前置通知

6、在Spring配置文件中开启自动代理

1
<aop:aspectj-autoproxy proxy-target-class="true"/>

开启自动代理后,凡是带有@Aspect注解的bean,Spring都会根据代理表达式生成目标对象的代理对象

proxy-target-class="true" 表示采用cglib动态代理。

proxy-target-class="false" 表示采用jdk动态代理。默认值是false。即使写成false,当没有接口的时候,也会自动选择cglib生成代理类。

7、使用目标对象(自动使用代理对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.powernode.spring6.test;

import com.powernode.spring6.service.OrderService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AOPTest {
@Test
public void testAOP(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj-aop-annotation.xml");
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.generate(); //直接使用目标对象的方法,会自动调用代理对象
}
}

8.3 通知类型

  • 前置通知:@Before 目标方法执行之前的通知
  • 后置通知:@AfterReturning 目标方法执行之后的通知
  • 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
  • 异常通知:@AfterThrowing 发生异常之后执行的通知
  • 最终通知:@After 放在finally语句块中的通知

环绕通知:

1
2
3
4
5
6
7
@Around("execution(* com.powernode.spring6.service.OrderService.*(..))")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始");
// 执行目标方法。
proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
}

是范围最大的通知

image-20260417155929029

8.4 多个切面的顺序

在切面类上添加@Order(数字)注解,数字越小,优先级越高。

8.5 通用切点

同类的通用切点

image-20260417161447675

跨类的通用切点

image-20260417161555535

如果一个切面需要有多个切点,怎么设置?

把每个切点单独写一个通用切点表达式,然后在切面的切点表达式中用||连接

image-20260417204642341

8.6 连接点 JoinPoint

是执行过程中的所有位置(方法),是切点的候选者。

触发通知时,Spring传入的参数是JoinPoint对象。切点是一组特定的连接点的集合。在代码中的体现为切点表达式

image-20260417170125138

8.7 Spring AOP 全注解开发

使用配置类代替xml文件

image-20260417170915331

image-20260417170956583

8.8 Spring AOP基于XML的实现

image-20260417201756380

九、Spring 事务

9.1 概述

事务:在一个业务流程中,需要多条DML数据修改(增删改)语句,这些语句必须同时成功或者同时失败

四个特性:ACID

9.2 Spring 对事务的实现

编程式事务

通过编写代码的方式来实现(很少使用)

声明式事务

  • 基于注解的方式
  • 基于XML配置的方式

9.3 事务管理器接口

PlatformTransactionManager接口:Spring事务管理器的核心接口。

在Spring6中它有两个实现:

  • DataSourceTransactionManager:支持JdbcTemplate、Mybatis、Hibernate等事务管理
  • JtaTransactionManager:支持分布式事务管理

image-20260418110144011

image-20260418110225869

image-20260418110321542

9.4 事务-注解方式

image-20260418110731997

9.5 事务的传播行为

在一个类中有a方法和b方法,a方法和b方法上都有事务。当a方法调用了b方法,事务是如何传递的?合并到一个事务,还是开启一个新的事务?

七种传播行为:

  • REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】
  • MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】
  • REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】
  • NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】
  • NEVER:以非事务方式运行,如果有事务存在,抛出异常【不支持事务,存在就抛异常】
  • NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】

image-20260418112240439

9.6 事务的隔离级别

image-20260418114400962

  • 读未提交:READ_UNCOMMITTED

  • 读提交:READ_COMMITTED

  • 可重复读:REPEATABLE_READ

  • 序列化:SERIALIZABL

不可重复读幻读的区别:

  1. 不可重复读是事务A多次读取同一条记录,但是结果不一样。(读到了事务B已经提交的内容)【一条记录内的值】
  2. 幻读是在同一事务内,事务A多次执行相同的范围查询,查询到的行数不一样。(读到了B已经提交货删除的记录)【一批记录的数量变化】

9.7 事务超时

@Transactional(timeout = 10)

超过10s如果事务中所有DML语句还没有执行完毕,就回滚。

默认值-1,表示没有时间限制。

这个时间计算的是最后一条DML语句结束的时间,如果最后一条DML语句在10s内结束,就不算超时。

9.8 只读事务

@Transactional(readOnly = true)

在这个事务中不能执行增删改语句,只允许执行select语句

作用:启动spring的优化策略,提高select语句执行效率

9.9 是否回滚

设置哪些异常回滚事务

@Transactional(rollbackFor = RuntimeException.class)

表示只有发生RuntimeExceptiopn该异常的子类才回滚

设置哪些异常不回滚事务

@Transactional(noRollbackFor = NullPointerException.class)

发生NullPointerException或该异常的子类异常不回滚,其他异常则回滚。

9.10 全注解开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.powernode.bank;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

/**
* @author 动力节点
* @version 1.0
* @className Spring6Config
* @since 1.0
**/
@Configuration
@ComponentScan("com.powernode.bank")
@EnableTransactionManagement
public class Spring6Config {

@Bean
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring6");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}

@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}

@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}

}