Spring 学习记录

Spring 学习记录

  • 1. Spring和SpringFrameWork
    • 1.1 广义的Spring
    • 2.1 狭义的Spring
    • 2.3 SpringFrameWork / Spring框架图
  • 2. Spring IOC容器(即上图中的Core Container)
    • 2.1 相关概念 (IOC DI 容器 组件)
    • 2.2 Spring IOC容器的作用
    • 2.3 Spring IOC容器接口和具体实现类
  • 3. Spring IOC 实践
    • 3.1 IOC / DI 一般步骤
    • 3.2 基于 XML配置 方法
      • 3.2.1 组件信息声明配置 (IOC) 和 依赖注入配置 (DI)
      • 3.2.2 IOC容器创建和使用
      • 3.2.3 组件周期方法配置
      • 3.2.4 FactoryBean的使用
    • 3.3 基于 注解配置 方法
      • 3.3.1 Spring提供的常见注解
      • 3.3.2 注解中BeanName的问题
      • 3.3.2 Autowired 和 Resourc 注解
    • 3.4 基于 配置类 方法
    • 3.5 三种方法总结
  • 4. Spring AOP
    • 4.1 AOP是什么
    • 4.2 为什么引入AOP
    • 4.3 主要应用场景
    • 4.4 Spring AOP 基于注解实现
      • 4.4.1 Spring AOP 底层技术组成
      • 4.3.2 通知增强的类型
      • 4.4.3 切点表达式
      • 4.4.4 代码示例
  • 5.Spring-tx
    • 5.1什么是Spring-tx?为什么需要Spring-tx?
    • 5.2 代码示例
    • 5.3 事务属性

1. Spring和SpringFrameWork

通常情况下,我们所说的Spring是狭义概念的Spring,即SpringFrameWork框架。

1.1 广义的Spring

广义上的 Spring 泛指以 Spring Framework 为基础的 Spring 技术栈

经过十多年的发展,Spring 已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等,其中 Spring Framework 是其他子项目的基础

2.1 狭义的Spring

狭义的 Spring 特指 Spring Framework,通常我们将它称为 Spring 框架
Spring Framework(Spring框架)是一个开源的应用程序框架,由SpringSource公司开发,最初是为了解决企业级开发中各种常见问题而创建的。它提供了很多功能,例如:依赖注入(Dependency Injection)、面向切面编程(AOP)、声明式事务管理(TX)等。其主要目标是使企业级应用程序的开发变得更加简单和快速,并且Spring框架被广泛应用于Java企业开发领域。

2.3 SpringFrameWork / Spring框架图

在这里插入图片描述
在这里插入图片描述

2. Spring IOC容器(即上图中的Core Container)

2.1 相关概念 (IOC DI 容器 组件)

  • IOC:Inversion of Control(控制反转)
    主要是针对对象的创建和调用控制而言的。当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权 。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
  • DI: Dependency Injection(依赖注入)
    组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。例如在A类内部需要引用B类,不需要A类的内部创建B类的对象,而是在容器内完成。在 Spring 中,DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter 方法注入和接口注入
  • IOC容器:IOC是是一种思想而不是某种技术 简而言之即对象不再由应用程序创建,而是由另外的独立的模块来创建,我们把这个独立的模块称为容器,由于应用了IOC思想,所以叫做IOC容器。Spring框架应用了IOC思想将对象的创建放在了独立的模块中,称为Spring IOC容器。
  • 组件:简而言之,组件是对象。但是对象不一定是组件

在这里插入图片描述

2.2 Spring IOC容器的作用

Spring 框架中的IOC容器负责创建组件、管理组件的依赖关系、存储组件,销毁组件。减少编码压力,让程序员更加专注进行业务编写。
容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、Java 注解或 Java 代码形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系。
在这里插入图片描述
配置元数据的方式

  • XML配置方式:是Spring框架最早的配置方式之一,通过在XML文件中定义Bean及其依赖关系、Bean的作用域等信息,让Spring IoC容器来管理Bean之间的依赖关系。该方式从Spring框架的第一版开始提供支持。
  • 注解方式:从Spring 2.5版本开始提供支持,可以通过在Bean类上使用注解来代替XML配置文件中的配置信息。通过在Bean类上加上相应的注解(如@Component, @Service, @Autowired等),将Bean注册到Spring IoC容器中,这样Spring IoC容器就可以管理这些Bean之间的依赖关系。
  • Java配置类方式:从Spring 3.0版本开始提供支持,通过Java类来定义Bean、Bean之间的依赖关系和配置信息,从而代替XML配置文件的方式。Java配置类是一种使用Java编写配置信息的方式,通过@Configuration、@Bean等注解来实现Bean和依赖关系的配置。

2.3 Spring IOC容器接口和具体实现类

  • 接口BeanFactory接口提供了配置框架和基本功能,ApplicationContext接口继承自BeanFactory接口,添加了更多特定于企业的功能。
  • 实现类:以下实现类都继承了某些父类,这些父类实现了ApplicatiContext接口。
    在这里插入图片描述
    例如:ClassPathXmlApplicationContext的类继承关系如下:引用自该文章
    在这里插入图片描述

3. Spring IOC 实践

3.1 IOC / DI 一般步骤

元数据配置——容器读取配置并实例化——获取对应的组件
元数据配置:此步骤只是进行配置而不进行具体的实例化
容器读取配置并实例化组件:此步骤容器会对配置读取进而实例化组件
获取对应的组件:此步骤是获取所需要的组件
在这里插入图片描述

3.2 基于 XML配置 方法

3.2.1 组件信息声明配置 (IOC) 和 依赖注入配置 (DI)

  • 基于无参构造函数在这里插入图片描述
public class HappyComponent {//默认包含无参数构造函数public void doWork() {System.out.println("HappyComponent.doWork");}
}
...
<bean id="happyComponent" class="com.local.ioc_01.HappyComponent"/>
  • 基于有参的构造函数
    如果是基本类型,就用value。如果是引用类型,就用ref。所ref的类型要在配置文件中已经配置过,并且ref的是对应类型的id。
    在这里插入图片描述
public class UserDao {
}public class UserService {private UserDao userDao; private int age; private String name;public UserService(int age , String name ,UserDao userDao) {this.userDao = userDao;this.age = age;this.name = name;}
}
<bean id="userDao" class="com.local.ioc_01.UserDao"/>
<bean id="userService" class="com.local.ioc_01.UserService"><constructor-arg name="name" value="zzz"/><constructor-arg name="age" value="12"/><constructor-arg name="userDao" ref="userDao"/>
</bean>
  • 基于静态工厂方法(创建对象的方法是静态方法)
    注:工厂方法——在方法内部创建对象,外部访问的时候只需要访问方法就可以,类似在“工厂”内部对对象进行创建,而使用者不用关心在工厂内部发生了什么。
    factory-method: 指定静态工厂方法,注意,该方法必须是static方法。
public class ClientService {public static ClientService createInstance() {return new ClientService();}
}
<bean id="clientService" class="com.local.ioc_01.ClientService" factory-method="createInstance"/>
  • 基于实例工厂方法(创建对象的方法是非静态方法)
    factory-bean属性:指定当前容器中工厂Bean 的名称。
    factory-method: 指定实例工厂方法名。注意,实例方法必须是非static的
public class DefaultServiceLocator {public ClientService createClientServiceInstance() {return new ClientService();}
}
<bean id="serviceLocator" class="com.local.ioc_01.DefaultServiceLocator"/>
<bean id="clientService2" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
  • 基于Setter方法依赖注入
public class MovieFinder {
}public class SimpleMovieLister {private MovieFinder movieFinder;private String movieName;public void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}public void setMovieName(String movieName){this.movieName = movieName;}
}
<bean id="movieFinder" class="com.local.ioc_02.MovieFinder"/>
<bean id="movieLister" class="com.local.ioc_02.SimpleMovieLister"><property name="movieFinder" ref="movieFinder"/><property name="movieName"   value="电影"/>
</bean>

3.2.2 IOC容器创建和使用

# spring-ioc-01.xml是要读取的配置文件
# getBean()中填写要创建的组件对应的id和对应的反射ClassPathXmlApplicationContext Context = new ClassPathXmlApplicationContext("spring-ioc-01.xml");
UserService userService = Context.getBean("userService", UserService.class);

3.2.3 组件周期方法配置

可以在组件类中定义方法,然后当IOC容器实例化和销毁组件对象的时候进行调用。这两个方法我们成为生命周期方法。
类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。
方法命名随意,但是要求方法必须是 public void 无形参列表

public class BeanOne {public void init(){System.out.println("init....");}public void destroy(){System.out.println("destroy....");}
}
<bean id="bean" class="com.local.ioc_03.BeanOne" init-method="init" destroy-method="destroy"/>
ClassPathXmlApplicationContext Context = new ClassPathXmlApplicationContext("spring-ioc-03.xml");
BeanOne bean = Context.getBean("bean", BeanOne.class);
Context.close();
//init....
//destroy....

3.2.4 FactoryBean的使用

FactoryBean 是接口。类实例化该接口后可以将创建复杂对象的过程存储在FactoryBean 的getObject方法中。
待定…

在这里插入代码片

3.3 基于 注解配置 方法

和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

3.3.1 Spring提供的常见注解

@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字,在语法层面没有区别。但是为了代码的可读性,不要随意乱起。
在这里插入图片描述

  • 使用注解的组件
@Controller
public class UserController {@Autowiredprivate UserService userService;}@Service
public class UserService {@Autowiredprivate UserDao userDao;
}@Repository
public class UserDao {
}
  • xml配置文件进行扫描
基本扫描
<context:component-scan base-package="com.atguigu.components"/>排除扫描
<context:component-scan base-package="com.atguigu.components"><!-- context:exclude-filter标签:指定排除规则 --><!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 --><!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 --><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>指定扫描
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<context:component-scan base-package="com.atguigu.ioc.components" use-default-filters="false"><!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 --><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  • 获取对象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("annotation01.xml");
UserController bean = context.getBean(UserController.class);

3.3.2 注解中BeanName的问题

使用 XML 方式管理 bean 的时候,每个 bean 都有一个唯一标识——id 属性的值,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。
默认情况:类名首字母小写就是 bean 的 id。例如:SoldierController 类对应的 bean 的 id 就是 soldierController。
还可以使用value属性指定:

@Controller(value = "tianDog")
public class SoldierController {
}

3.3.2 Autowired 和 Resourc 注解

  • AutoWired注解
    在成员变量上直接标记@Autowired注解即可。容器创建对象的时候会自动寻找对应的组件进行注入。注入流程如下:
    在这里插入图片描述
@Controller(value = "tianDog")
public class SoldierController {@Autowired@Qualifier(value = "maomiService222")// 根据面向接口编程思想,使用接口类型引入Service组件private ISoldierService soldierService;//依赖注入的时候,去寻找value值对应的注解
  • Resource注解
    @Resource注解默认根据 Bean名称装配,未指定name时,使用属性名作为name通过name找不到的话会自动启动通过类型装配。
    @Autowired注解默认根据类型装配,如果想根据名称装配,需要配合@Qualifier注解一起用
    @Autowired注解是Spring提供的注解,@Resource注解是JDK扩展包中的。【高于JDK11或低于JDK8需要引入以下依赖
<dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>2.1.1</version>
</dependency>
@Controller
public class UserController {/*** 1. 如果没有指定name,先根据属性名查找IoC中组件xxxService* 2. 如果没有指定name,并且属性名没有对应的组件,会根据属性类型查找* 3. 可以指定name名称查找!  @Resource(name='test') == @Autowired + @Qualifier(value='test')*/@Resourceprivate UserService UserService;
}

3.4 基于 配置类 方法

完全注解开发:Spring 完全注解配置(Fully Annotation-based Configuration)是指通过 Java配置类 代码来配置 Spring 应用程序,使用注解来替代原本在 XML 配置文件中的配置。相对于 XML 配置,完全注解配置具有更强的类型安全性和更好的可读性

  • 配置类:使用 @Configuration 注解将一个普通的类标记为 Spring 的配置类
@Configuration
@ComponentScan(basePackages = {"com.local"})
// 上述代替了使用xml文件配置
public class configuration {
}
  • IOC容器创建对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configuration.class);
UserController bean = context.getBean(UserController.class);

3.5 三种方法总结

  • XML方式配置
    在这里插入图片描述

  • XML+注解方式配置
    在这里插入图片描述

  • 完全注解方式配置
    在这里插入图片描述

4. Spring AOP

4.1 AOP是什么

AOP:Aspect Oriented Programming(面向切面编程)面向切面编程是一种思维,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

4.2 为什么引入AOP

某种程度AOP上完善和解决OOP的非核心代码冗余和不方便统一维护问题。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

4.3 主要应用场景

  1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
  2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
  3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
  4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
  5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
  6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
  7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。

4.4 Spring AOP 基于注解实现

4.4.1 Spring AOP 底层技术组成

在这里插入图片描述

  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
  • cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
  • AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。

4.3.2 通知增强的类型

即表示将切面中的方法切入到核心方法中的哪里去。

- 前置通知:在被代理的目标方法前执行 @Before
- 返回通知:在被代理的目标方法成功结束后执行 @After
- 异常通知:在被代理的目标方法异常结束后执行 @AfterThrowing
- 后置通知:在被代理的目标方法最终结束后执行 @AfterReturning
- 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置 @Around

4.4.3 切点表达式

  • 什么是切点表达式
    在准备好切面后,需要确定将切面切入到哪里,即切入到核心方法的哪里,切点表达式即指定说明了切入的点。
  • 切点表达式语法
  • 第一位:execution( ) 固定开头
  • 第二位:方法访问修饰符
public private 直接描述对应修饰符即可
  • 第三位:方法返回值
int String void 直接描述返回值类型
注意:特殊情况 不考虑 访问修饰符和返回值execution(* * ) 这是错误语法execution(*) == 你只要考虑返回值 或者 不考虑访问修饰符 相当于全部不考虑了
  • 第四位:指定包的地址
 固定的包: com.atguigu.api | service | dao单层的任意命名: com.atguigu.*  = com.atguigu.api  com.atguigu.dao  * = 任意一层的任意命名任意层任意命名: com.. = com.atguigu.api.erdaye com.a.a.a.a.a.a.a  ..任意层,任意命名 用在包上!注意: ..不能用作包开头   public int .. 错误语法  com..找到任何包下: *..
  • 第五位:指定类名称
固定名称: UserService
任意类名: *
部分任意: com..service.impl.*Impl
任意包任意类: *..*
  • 第六位:指定方法名称
语法和类名一致
任意访问修饰符,任意类的任意方法: * *..*.*
  • 第七位:方法参数
第七位: 方法的参数描述具体值: (String,int) != (int,String) 没有参数 ()模糊值: 任意参数 有 或者 没有 (..)  ..任意参数的意识部分具体和模糊:第一个参数是字符串的方法 (String..)最后一个参数是字符串 (..String)字符串开头,int结尾 (String..int)包含int类型(..int..)
  • 切点表达式重用
    Q:为什么要对切点表达式重用?
    A:当多个切面表达式相同时,后期进行修改维护较麻烦,例如以下:前2个和后2个的切面表达式相同,因此将切点表达式提取到同一个类中,方便重用。
@Before(value = "execution(public int *..Calculator.sub(int,int))")
..........
@AfterReturning(value = "execution(public int *..Calculator.sub(int,int))")
.........
@AfterThrowing(value = "execution(* *..*Service.*(..))")
.........
@After(value="execution(* *..*Service.*(..))")
......

Q:如何重用?如何重用后引用?

// 重用
@Component
public class PointCut {@Pointcut(value = "execution(public int *..Calculator.sub(int,int))")public void GlobalPointCut(){}@Pointcut(value = "execution(* *..*Service.*(..))")public void SecondPointCut(){}
}//引用
@Before(value = "GlobalPointCut")  //value中是方法名
public void printLogBeforeCoreOperation() {}

4.4.4 代码示例

在这里插入图片描述

//定义计算接口
public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);
}//接口的核心代码实现类(只实现核心代码而忽略一些打印输出和异常处理的代码)
@Component
public class CalculatorPureImpl implements Calculator {  @Overridepublic int add(int i, int j) {return i + j;}  @Overridepublic int sub(int i, int j) {return i - j;}@Overridepublic int mul(int i, int j) {return i * j;}@Overridepublic int div(int i, int j) {return i / j;}
}//重用切面表达式
@Component
public class MyPointCut {@Pointcut(value = "execution(* service.impl.*.*(..))")public void pointCutOne(){}
}//切面
@Component
@Aspect
public class LogAdvice {@Before("PointCut.MyPointCut.pointCutOne()")public void start(){System.out.println("start");}@After("PointCut.MyPointCut.pointCutOne()")public void end(){System.out.println("end");}@AfterThrowing("PointCut.MyPointCut.pointCutOne()")public void error(){System.out.println("error");}
}//配置类扫描和开启aspectj注解
@Configuration
@ComponentScan({"service","advice","config"}) //扫描
@EnableAspectJAutoProxy //开启aspectj注解
public class JavaConfig {
}//测试
@SpringJUnitConfig(value = JavaConfig.class)
public class SpringAopTest {@Autowiredprivate Calculator calculator;@Testpublic void test1(){int res=calculator.add(1,1);System.out.println(res);}
}
//输出
start
end
2

5.Spring-tx

5.1什么是Spring-tx?为什么需要Spring-tx?

  • Spring-tx是Spring框架支持以声明性的方式管理事务,而不是编程式的方式。将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性
try {// 开启事务:关闭事务的自动提交conn.setAutoCommit(false);// 核心操作// 业务代码// 提交事务conn.commit();  
}catch(Exception e){  // 回滚事务conn.rollBack();
}finally{ // 释放数据库连接conn.close();
}
  • 编程式事务:手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。
  • 声明式事务:使用注解或 XML 配置的方式来控制事务的提交和回滚。开发者只需要添加配置即可, 具体事务的实现由第三方框架实现,避免我们直接进行事务操作。

5.2 代码示例

在这里插入图片描述

//jdbc.properties
atguigu.url=jdbc:mysql://localhost:3306/fruitdb
atguigu.driver=com.mysql.cj.jdbc.Driver
atguigu.username=root
atguigu.password=123456//javaconfig
@Configuration
@ComponentScan({"Dao","Service"})
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement //开启事务注解的支持
public class JavaConfig {@Value("${atguigu.driver}")private String driver;@Value("${atguigu.url}")private String url;@Value("${atguigu.username}")private String username;@Value("${atguigu.password}")private String password;//druid连接池@Beanpublic DataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Bean//jdbcTemplatepublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}//使用事务管理器 //事务管理器需要数据库连接信息datasource@Beanpublic DataSourceTransactionManager transactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}//FruitDao
@Repository
public class FruitDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void updatePriceByName(String name,Integer price){String sql = "update t_fruit set price = ? where fname = ? ;";int rows = jdbcTemplate.update(sql, price,name);}public void updateRemarkByName(String name,String remark){String sql = "update t_fruit set remark= ? where fname = ? ;";jdbcTemplate.update(sql,remark,name);}
}//FruitService 
@Service
public class FruitService {@Autowiredprivate FruitDao fruitDao;@Transactional //添加事务注解public void changeInfo(){fruitDao.updatePriceByName("苹果",30);int i=1/0; //这里会报错 那么整个事务将会回滚,2次修改信息都将失败 如果没有事务 那么第一次将修改成功而第二次失败fruitDao.updateRemarkByName("苹果","ok");}
}

上述代码在FruitService的方法中添加了Transactionnal注解,该注解可以作用与类和方法上,作用于类上说明对类内的方法都生效,作用于方法则只对方法生效。

5.3 事务属性

  • 只读
    在Transactionnal注解中设置属性readOnly属性为True,默认值为False
    在这里插入图片描述
@Transactional(readOnly = true)
  • 超时时间
    程序运行过程中因为某些原因卡住占用资源,设置超时时间,事务运行的时间超过超过设置的超时时间则回滚,释放资源。通过timeout属性设置。默认值是-1,即无限。
    在这里插入图片描述
@Transactional(timeout = 3)
  • 事务异常
    关于异常的分类可以参考此文章-异常分类总结
    默认只针对运行时异常回滚,编译时异常不回滚
    rollbackForClassName:指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
    noRollbackForClassName:指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
public class FruitService {@Autowiredprivate FruitDao fruitDao;@Transactional(noRollbackFor = ArithmeticException.class) //添加事务注解 发生该异常时不回滚public void changeInfo(){fruitDao.updatePriceByName("苹果",30);int i=1/0; //这里会报错 但是设置为不回滚 所以第一条修改成功,但是第二条修改失败fruitDao.updateRemarkByName("苹果","ok");}
}
  • 隔离级别
    数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:
  1. 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
  2. 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
  3. 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
  4. 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
@Transactional(isolation = Isolation.REPEATABLE_READ)
  • 事务传播
    在这里插入图片描述
    @Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是
Propagation propagation() default Propagation.REQUIRED;

在这里插入图片描述

//jdbc.properties
atguigu.url=jdbc:mysql://localhost:3306/fruitdb
atguigu.driver=com.mysql.cj.jdbc.Driver
atguigu.username=root
atguigu.password=123456//javaconfig
@Configuration
@ComponentScan({"Dao","Service"})
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement //开启事务注解的支持
public class JavaConfig {@Value("${atguigu.driver}")private String driver;@Value("${atguigu.url}")private String url;@Value("${atguigu.username}")private String username;@Value("${atguigu.password}")private String password;//druid连接池@Beanpublic DataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Bean//jdbcTemplatepublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}//使用事务管理器 //事务管理器需要数据库连接信息datasource@Beanpublic DataSourceTransactionManager transactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}//FruitDao
@Repository
public class FruitDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void updatePriceByName(String name,Integer price){String sql = "update t_fruit set price = ? where fname = ? ;";int rows = jdbcTemplate.update(sql, price,name);}public void updateRemarkByName(String name,String remark){String sql = "update t_fruit set remark= ? where fname = ? ;";jdbcTemplate.update(sql,remark,name);}
}//FruitService
@Service
public class FruitService {@Autowiredprivate FruitDao fruitDao;@Transactional(noRollbackFor = ArithmeticException.class,propagation = Propagation.REQUIRES_NEW) //添加事务注解 发生该异常时不回滚 并且修改默认传播public void changePrice(){fruitDao.updatePriceByName("香蕉",50);int i=1/0; //这里会报错 但是设置了不回滚}@Transactionalpublic void changeRemark(){fruitDao.updateRemarkByName("苹果","good");}
}//TopService 整合的Service
@Service
public class TopService {@Autowiredprivate FruitService fruitService;@Transactionalpublic void changeInfo(){fruitService.changePrice();fruitService.changeRemark();}
}//测试
@Test
public void test(){topService.changeInfo();
}结果:父事务默认的传播行为,changePrice方法中修改了默认的传播方法,并且遇到运行错误时不回滚。
那么changePrice方法则忽略父方法中的传播行为,独立创建事务。
结果是成功修改了price而没有修改remark。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/711527.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flask 数据库迁移报错 Error: No such command ‘db‘.

初学FLASK&#xff0c;使用pycharm的terminal 启动&#xff0c;实现数据库迁移 文件结构 项目启动文件不在一级目录pycharm>terminal启动 由于自己初入 python flask 很多东西并不懂&#xff0c;只能依葫芦画瓢&#xff0c;使用如下命令,输入完第一行命令执行没有任何错误…

Vue ElementUI 修改消息提示框样式—messageBox 的大小

在窄屏模式下&#xff08;移动端或pda&#xff09;&#xff0c;提示框的宽度太宽&#xff0c;会出现显示不完全的问题。 应当如何修改 ElementUI 的样式呢&#xff1f; open() {this.$confirm(window.vm.$i18n.t("tips.conLogOut"),window.vm.$i18n.t("tips.tip…

11-Linux部署集群准备

Linux部署集群准备 介绍 在前面&#xff0c;我们所学习安装的软件&#xff0c;都是以单机模式运行的。 后续&#xff0c;我们将要学习大数据相关的软件部署&#xff0c;所以后续我们所安装的软件服务&#xff0c;大多数都是以集群化&#xff08;多台服务器共同工作&#xff…

【机器学习实战1】泰坦尼克号:灾难中的机器学习(一)数据预处理

&#x1f338;博主主页&#xff1a;釉色清风&#x1f338;文章专栏&#xff1a;机器学习实战&#x1f338;今日语录&#xff1a;不要一直责怪过去的自己&#xff0c;她曾经站在雾里也很迷茫。 &#x1f33c;实战项目简介 本次项目是kaggle上的一个入门比赛 &#xff1a;Titani…

锚索测力计数据处理与分析:MCU自动测量单元的应用

锚索测力计作为一种重要的工程监测工具&#xff0c;在桥梁、大坝、隧道等结构物的健康监测中发挥着日益重要的作用。如何高效、准确地处理和分析&#xff0c;锚索测力计所获取的数据成为了工程师们面临的重要问题。近年来&#xff0c;随着微控制器(MCU)技术的快速发展&#xff…

51-n皇后(回溯算法)

题目 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。 每一…

前端开发项目必备神器之node工具整理

前言&#xff1a; 在我们开发项目中&#xff0c;node是我们必备的工具&#xff0c;在为了适应各种不同的开发需求的同时&#xff0c;node也有很多好用的插件提供给我们&#xff0c;这里整理个人的使用分享给大家&#xff01; 一、node相关 1、node官方网站&#xff0c;可以安装…

模拟算法题练习(二)(DNA序列修正、无尽的石头)

&#xff08;一、DNA序列修正&#xff09; 问题描述 在生物学中&#xff0c;DNA序列的相似性常被用来研究物种间的亲缘关系。现在我们有两条 DNA序列&#xff0c;每条序列由 A、C、G、T 四种字符组成&#xff0c;长度相同。但是现在我们记录的 DNA序列存在错误&#xff0c;为了…

ubuntu基础操作(1)-个人笔记

搜狗输入法Linux官网-首页搜狗输入法for linux—支持全拼、简拼、模糊音、云输入、皮肤、中英混输https://pinyin.sogou.com/linux 1.关闭sudo密码&#xff1a; 终端&#xff08;ctrl alt t&#xff09;输入 sudo visudo 打开visudo 找到 %sudo ALL(ALL:ALL) ALL 这一行…

羊大师分享,羊奶奶有哪些对健康有益的喝法?

羊大师分享&#xff0c;羊奶奶有哪些对健康有益的喝法&#xff1f; 羊奶奶有多种对健康有益的喝法&#xff0c;以下是一些建议&#xff1a; 直接饮用&#xff1a;将羊奶直接煮沸后饮用&#xff0c;可以保留羊奶中的营养成分&#xff0c;为身体提供全面的滋养。羊奶的丰富蛋白质…

代码随想录算法训练营第二十八天补|93.复原IP地址 ● 78.子集 ● 90.子集II

组合问题&#xff1a;集合内元素的组合&#xff0c;不同集合内元素的组合 分割问题&#xff1a;本质还是组合问题&#xff0c;注意一下如何分割字符串 回溯模板伪代码 void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择&#xff1a;本层集合中元素&#xf…

【六袆 - MySQL】MySQL 5.5及更高版本中,InnoDB是新表的默认存储引擎;

InnoDB 这是一个MySQL组件&#xff0c;结合了高性能和事务处理能力&#xff0c;以确保可靠性、健壮性和并发访问。它体现了ACID设计哲学。它作为一个存储引擎存在&#xff0c;处理使用ENGINEINNODB子句创建的或修改的表。请参阅第14章“InnoDB存储引擎”以获取有关架构细节和管…

【解决】虚幻导入FBX模型不是一个整体

问题&#xff1a; 现在有一个汽车的fbx模型&#xff0c;导入虚幻引擎&#xff0c;导入后变成了很多汽车零件模型。 解决&#xff1a; 把“合并网格体”勾选上&#xff0c;解决问题。

Unity 佳能SDK 及数据获取

1. 填写信息跟官方申请SDK,大概1-2个工作日会邮件回复你 佳能(中国)- 佳定制(佳能影像产品),SDK,EDSDK,CCAPI,软件开发包下载 2. 将SDK这两个文件放到 Unity Plugins文件夹 3. 把CameraControl 下面只要是绿色的 .cs 文件都复制到Unity 中

ElasticSearch搜索引擎使用指南

一、ES数据基础类型 1、数据类型 字符串 主要包括: text和keyword两种类型&#xff0c;keyword代表精确值不会参与分词&#xff0c;text类型的字符串会参与分词处理 数值 包括: long, integer, short, byte, double, float 布尔值 boolean 时间 date 数组 数组类型不…

基于ssm学生公寓管理系统的设计与开发论文

学生公寓管理系统的设计与实现 摘要 如今&#xff0c;科学技术的力量越来越强大&#xff0c;通过结合较为成熟的计算机技术&#xff0c;促进了学校、医疗、商城等许多行业领域的发展。为了顺应时代的变化&#xff0c;各行业结合互联网、人工智能等技术&#xff0c;纷纷开展了…

P1160 队列安排题解

题目 一个学校里老师要将班上N个同学排成一列&#xff0c;同学被编号为1∼N&#xff0c;他采取如下的方法&#xff1a; 先将1号同学安排进队列&#xff0c;这时队列中只有他一个人&#xff1b; 2∼N号同学依次入列&#xff0c;编号为i的同学入列方式为&#xff1a;老师指定编…

下载huggingface数据集到本地并读取.arrow文件遇到的问题

文章目录 1. 524MB中文维基百科语料&#xff08;需要下载的数据集&#xff09;2. 下载 hugging face 网站上的数据集3. 读取 .arrow 文件报错代码4. 纠正后代码 1. 524MB中文维基百科语料&#xff08;需要下载的数据集&#xff09; 2. 下载 hugging face 网站上的数据集 要将H…

MATLAB环境下一种新颖的类脉冲信号的高分辨率时频分析方法

一般情况下&#xff0c;机械振动信号或地震信号是非平稳的。而传统傅立叶变换只能应用于平稳信号分析&#xff0c;故不适用于非平稳信号。所以&#xff0c;我们需要采用时频分析方法。时频分析方法能达到同时在时间域和频率域对信号进行分析的目的&#xff0c;得到信号在不同时…

Python爬取网站视频资源

思路&#xff1a; 在界面找到视频对应的html元素位置&#xff0c;观察发现视频的url为https://www.pearvideo.com/video_视频的id&#xff0c;而这个id在html中的href中&#xff0c;所以第一步需要通过xpath捕获到所需要的id 在https://www.pearvideo.com/video_id的页面&…