概念
Spring是轻量级的开源的JavaEE框架。
Spring可以解决企业应用开发的复杂性。
Spring有两个核心部分:IOC、AOP
- IOC:控制反转,把创建对象的过程交给Spring进行管理。
- AOP:面向切面,不修改源代码进行功能增强。
Spring特点:
- 方便解耦,简化开发。
- AOP编程支持
- 方便程序测试
- 方便与其他框架进行整合
- 方便进行事务操作
- 降低API开发难度
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
- Spring Core:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
- Spring Context:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
- Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
- Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
- Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
IOC容器
概念
什么是IOC:
- 控制反转,把对象创建和对象之间的调用过程(即对象之间的依赖关系)交给Spring进行管理。
- 目的:降低耦合度
本质:
**控制反转(Inversion of Control,IOC),是一种设计思想,DI(依赖注入)**是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。
总结:
原本类与类之间的依赖关系是由我们程序员手动绑定的,即在类中手动去new一个其他类的对象。
而现在spring的BeanFactory中有一个beanMap存放着所有的bean实例,通过XML配置文件或注解指定类与类之间的依赖关系,从而将一个bean实例赋值到另一个bean实例的其中一个字段中。
BeanFactory中的beanMap称之为IOC容器,而通过配置实现bean之间的依赖关系则称之为依赖注入(Dependency Injection,DI)
因此,我们转移(改变)了这些类它们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转。
底层原理
第一步:XML配置文件,配置创建的对象
1 | <bean id="user" class="com.example.demo.User" p:bname="abc"/> |
第二步,创建工厂类
1 | class Userfactory{ |
接口
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。
Spring提供IOC容器的两种实现方式(两个接口):
BeanFactory:IOC肉搏国企基本实现,是Spring内部的使用接口,不提供开发人员使用
加载配置文件时不会创建对象,在获取对象(使用)时才去创建。
ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。
加载配置文件时就会把配置文件中的对象进行创建,即在服务器启动时就创建好对应的Bean对象。
常用实现类
ClassPathXmlApplicationContext
构造方法传递配置文件名,需提供类路径。
FileSystemXmlApplicationContext
构造方法传递配置文件名,需提供全路径。
Bean管理
所谓Bean管理,即管理Bean对象从创建到销毁的整个生命周期。 在Spring之前是由程序员来手动管理这一过程,而现在交给Spring进行操作。
- Spring创建对象
- Spring注入属性
基于XML
创建对象
通过bean标签创建对象,标签内添加对应property标签实现属性注入。
标签属性:
- id:唯一标识
- class:包类路径
创建对象时,默认执行无参构造方法完成对象创建。
属性注入
DI依赖注入,就是注入属性
使用set方法进行注入
具体类
1 | package com.example.demo; |
xml配置文件
标签属性:
- name:类里面属性名
- value:注入属性的值
1 | <!--set方法注入属性--> |
使用有参构造进行注入
创建具体类
1 | package com.example.demo; |
XML配置文件
标签属性:
- name:参数名
- value:要注入参数的值
- index:参数索引(从0开始)
1 | <bean id="orders" class="com.example.demo.Orders"> |
p名称空间注入
简化了set属性注入
1 |
|
注入空值
1 | <property name="address"> |
注入特殊符号
1.使用实体引用进行转移
2.把带特殊符号内容放到**< ![CDATA[ ]] >**,将其中内容作为属性值,而不是标签符号
1 | <!--把带特殊符号内容放到CDATA--> |
外部bean
标签属性:
name属性:类里面属性名称
ref属性:创建userDao对象bean标签id值
service类
1 | package com.example.demo.service; |
XML配置文件
1 | <bean id="userDao" class="com.example.demo.dao.UserDaoImpl"></bean> |
内部bean
1 | <!--内部bean--> |
级联赋值
内部级联赋值
1 | <!--内部bean--> |
外部级联赋值
1 | <bean id="emp" class="com.example.demo.bean.Emp"> |
注入集合属性
创建具体类
1 | package com.example.demo.bean; |
XML配置文件
1 | <!--集合类型属性注入--> |
提取集合类型属性
1 |
|
FactoryBean
Spring有两种类型Bean:
- 普通Bean:在配置文件中定义的bean类型就是返回类型
- 工厂Bean:在配置文件中定义的bean类型可以和返回类型不一样
- 创建类,让这个类作为工厂bean,实现接口FactoryBean
- 实现接口里面的方法,在实现的方法中定义返回的bean类型
1 | package com.example.demo.bean; |
XML配置文件
1 | <bean name="mybean" class="com.example.demo.bean.Mybean"> |
bean作用域
在spring里面,可以设置创建bean实例是单实例还是多实例。默认为单实例。
标签属性值:
- scope:生命周期
- singleton 单实例,加载Spring配置文件时就会创建单实例对象(饿汉式)
- prototype 多实例,在调用getBean方法时创建多实例对象(饿汉式)
- request 把创建的对象放入请求作用域
- session 把创建的对象放入session作用域
1 | <!--singleton 单实例 饿汉式 |
bean生命周期
从bean对象创建到销毁的过程称为bean生命周期。
- 通过构造器创建bean实例(无参构造)
- 为bean对象的属性设置值和对其他bean的引用(调用set方法)
- 把bean实例传递给bean后置处理器的方法
- 调用bean的初始化方法(需要进行配置)
- 把bean实例传递给bean后置处理器的方法
- bean可以使用了(对象获取到了)
- 当容器关闭时,调用bean的销毁的方法(需要进行配置)
具体类
1 | package com.example.demo.bean; |
后置处理器实现类
1 | package com.example.demo.bean; |
测试方法
1 |
|
XML配置文件
标签属性:
- init-method 该bean对象初始化时执行的方法
- destroy-method 该bean对象销毁时执行的方法
1 | <bean name="life" class="com.example.demo.bean.Life" init-method="initMethod" destroy-method="destroyMethod"> |
XML自动装配
根据指定装配规则(属性名称或属性类型),Spring自动将匹配的属性值进行注入
标签属性:
- autowire
- byName 根据属性名称注入,注入值bean的id值和类属性名称一样
- byType 根据属性类型注入
1 | <bean id="emp" class="com.example.demo.autowire.Emp" autowire="byName"/> |
外部属性文件
德鲁伊连接池配置
直接配置
1 | <!-- 直接配置德鲁伊连接池--> |
引入外部属性文件配置
创建外部属性文件,properties格式文件
1 | prop.driverClass=com.mysql.cj.Driver |
XML配置文件
1 |
|
基于注解
什么是注解:
- 注解是代码特殊标记。格式:
@注解名称(属性名称=属性值,属性名称=属性值...)
- 注解可以作用在类、方法、属性上
- 目的是简化XML配置
Spring针对Bean管理中创建对象提供注释
@Component
@Service
@Controller
@Repository
创建对象
开启组件扫描
如果扫描多个包,则使用逗号隔开。或扫描包上层目录。
过滤:
use-default-filters="false"
表示不使用默认过滤器
include-filter
包含哪些内容exclude-filter
不包含哪些内容标签属性:
- type 类型
- expression 具体表达式
1 |
|
属性注入
针对对象属性:
- @Autowired 根据属性类型进行自动装配
- @Qualifier 根据属性名称进行注入
- @Resource 可以根据类型注入,也可以根据名称注入
针对普通类型属性:
- @Value 注入普通类型属性
单一个@Resource作用相当于@Autowired。
给@Resource设置name属性值则相当于
@Autowired
@Qualifier(name)一起用
1 | package com.example.demo.service; |
开启组件扫描环节还可以用注解替代
@Configuration表示该类为配置类,代替XML配置文件
1 |
|
测试类
AnnotationConfigApplicationContext构造方法的参数是配置类的class
1 |
|
AOP
概念
AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
AOP使用场景
AOP用来封装横切关注点,具体可以在下面的场景中使用:
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务
AOP术语
web层级设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。
切面(Aspect)
:一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。连接点(Joinpoint)
: 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。通知(Advice)
: 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice切入点(Pointcut)
: 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上引入(Introduction)
: 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口目标对象(Target Object)
: 包含连接点的对象。也被称作被通知或被代理对象。POJOAOP代理(AOP Proxy)
: AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。织入(Weaving)
: 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
根据定义, Joint point
是所有可能被织入 Advice
的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 Joint point
.即指哪些目标函数可以被拦截。
所有的方法(joint point) 都可以织入 Advice
, 但是我们并不希望在所有方法上都织入 Advice
, 而 Pointcut
的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice
.
Advice
是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 Joint point
上的.
Aspect
是 point cut 与 Advice
的组合
在 Spring AOP 中 Joint point
指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 Joint point
, 通过 point cut, 我们就可以确定哪些 Joint point
可以被织入 Advice
通俗描述:
连接点:类里面哪些方法可以被增强
切入点:实际真正增强的方法
通知(增强):实际增强的逻辑部分
通知有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
切面:把通知应用到切入点的过程
底层原理
AOP底层使用动态代理
- 有接口情况,使用JDK动态代理
- 无接口情况,使用CGLIB动态代理
JDK动态代理
使用Proxy类里面的方法创建代理对象
1.调用newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
该方法有三个参数:
- 类加载器
- 增强方法所在类,这个类实现的接口。支持多个接口
- 实现这个接口InvocationHandler ,创建代理对象,写增强的方法。
底层代码实现
1 | package com.example.demo.proxy; |
AOP操作
Spring框架一般基于AspectJ实现AOP操作
AspectJ不是Spring组成部分,是一个独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作。
基于AspectJ实现AOP操作:
- 基于XML配置文件实现
- 基于注解方式实现
切入点表达式
切入点表达式作用:知道对哪个类里面的哪个方法进行增强。
语法结构:
execution([权限修饰符] [返回类型] [类全路径].[方法名称] ([参数列表]))
例子1:对com.yumo.dao.BookDao类里面的add方法进行增强:
execution(* com.yumo.dao.BookDao.add(..))
*
表示匹配所有- 返回类型可省略不写
..
在参数列表表示方法中的参数
例子2:对com.yumo.dao.BookDao类里面的所有方法进行增强:
execution(* com.yumo.dao.BookDao.*(..))
例子3:对com.yumo.dao包里面的所有类,类里面的所有方法进行增强:
execution(* com.yumo.dao.*.*(..))
注解方式
1、创建类,在类里面定义方法
1 | package com.example.demo.aopanno; |
2、创建增强类(编写增强逻辑)
在增强类里面创建方法,让不同放啊代表不同通知类型
3、进行通知的配置
- 在Spring配置文件中,开启注解扫描;或创建配置类使用注解。
1 |
|
- 使用注解创建User和UserProxy对象
@Component
- 在增强类上面添加注解
@AspectJ
- 在Spring配置文件中开启生成代理对象
1 |
|
或在增强类上使用注解@EnableAspectJAutoProxy
4、配置不同类型的通知
在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式进行配置。
5、相同的切入点抽取 @Pointcut
6、在多个增强类对同一个被增强类的同一切入点进行增强时,设置增强类优先级。
在增强类上面添加@Order(数字类型值)
,数字类型值越小,优先级越高。
1 | package com.example.demo.aopanno; |
配置文件方式
1、创建两个类,增强类BookProxy和被增强类Book,创建方法
1 | package com.example.demo.aopxml; |
1 | package com.example.demo.aopxml; |
2、在spring配置文件中创建两个类对象
1 | <bean id="book" class="com.example.demo.aopxml.Book"/> |
3、在spring配置文件中配置切入点
1 | <aop:config> |
事务管理
概念
事务是用户定义的数据库操作的集合,这些操作作为一个完整的有机工作单元,要么全部正确执行,要么全部不执行。
四大特性:
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
作用
以银行转账为例,若业务方法没有事务时,假如在业务方法执行时发生异常,有可能会造成数据库数据不一致。
解决方法是在业务方法中加入事务。若发生异常,事务回滚(回滚方法由Spring事务自动完成,不需要自定义)。
常用API
1)**PlatformTransactionManager
**:平台事务管理器
Spring进行事务操作时候,主要使用一个PlatformTransactionManager接口,它表示事务管理器,即真正管理事务的对象。
Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring针对不同的持久化框架,提供了不同PlatformTransactionManager接口的实现类:
org.springframework.jdbc.datasource.DataSourceTransactionManager :使用 Spring JDBC或MyBatis 进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager :使用 Hibernate版本进行持久化数据时使用
2)Spring的这组接口是如何进行事务管理的
平台事务管理器根据事务定义的信息进行事务的管理,事务管理的过程中产生一些状态,将这些状态记录到TrancactionStatus里面。
具体操作
在spring中进行事务管理操作有两种方式:
- 编程式事务管理
- 声明式事务管理
- 基于注解方式
- 基于xml配置文件方式
声明式事务管理
基于XML
在Spring的配置文件中进行配置
1、开启组件扫描
1 | <context:component-scan base-package="com.example"></context:component-scan> |
2、配置数据库连接池
1 | <context:component-scan base-package="com.example"></context:component-scan> |
3、配置JdbcTemplate
1 | <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> |
4、配置事务管理器
1 | <!--创建事务管理器--> |
5、配置通知
1 | <!--配置通知--> |
6、配置切入点和切面
1 | <!--配置切入点和切面--> |
基于注解
1、开启组件扫描
1 | <context:component-scan base-package="com.example"></context:component-scan> |
2、配置数据库连接池
1 | <context:component-scan base-package="com.example"></context:component-scan> |
3、配置JdbcTemplate
1 | <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> |
4、配置事务管理器
1 | <!--创建事务管理器--> |
5、开启事务注解
(1)引入名称空间tx
(2)开启事务注解
1 | <!--开启事务注解--> |
6、在Service类上面或类里面方法上面添加事务注解**@Transactional
**
- 如果加在类上,表示这个类里面所有的方法都添加事务
- 如果加在方法上,表示为这个方法添加事务
其底层原理通过AOP实现
该注解可添加的参数如下:
TransactionDefinition:事务定义信息
事务定义信息有:
- 隔离级别
- 传播行为
- 超时信息
- 是否只读
事务传播行为
propagation 事务传播行为
多事务方法直接进行调用,这个过程中事务是如何进行管理的
- 事务方法:对数据库表数据进行变化的操作
保证在同一个事务中
PROPAGATION_REQUIRED:required , 必须。支持当前事务,如果不存在,就新建一个(默认)
如果有事务在运行,当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行。
PROPAGATION_SUPPORTS:supports ,支持。支持当前事务,如果不存在,就不使用事务
如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中。
PROPAGATION_MANDATORY:mandatory ,强制。支持当前事务,如果不存在,就抛出异常
当前的方法必须运行在事务内部,若没有正在运行的事务,则抛出异常。
保证没有在同一个事务中
PROPAGATION_REQUIRES_NEW:requires_new,必须新的。如果有事务存在,挂起当前事务,创建一个新的事务
当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起。
PROPAGATION_NOT_SUPPORTED:not_supported ,不支持。以非事务方式运行,如果有事务存在,挂起当前事务
当前的方法不应该运行在事务中,如果有运行的事务,将它挂起。
PROPAGATION_NEVER:never,从不。以非事务方式运行,如果有事务存在,抛出异常
当前的方法不应该运行在事务中,如果有运行的事务,则抛出异常。
PROPAGATION_NESTED:nested ,嵌套。如果当前事务存在,则嵌套事务执行
如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。否则就启动一个新的事务,并在它自己的事务内运行。
隔离级别
isolation 事务隔离级别
隔离级别:定义了一个事务可能受其他并发事务影响的程度。
并发事务引起的问题:典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致以下的问题。
脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
READ UNCOMMITTED (读未提交) |
有 | 有 | 有 |
READ COMMITTED (读已提交) |
无 | 有 | 有 |
REPEATABLE (可重复读)(默认) |
无 | 无 | 有 |
SERIALIZABLE (串行化) |
无 | 无 | 无 |
解决读问题:设置事务的隔离级别
- 未提交读:脏读,不可重复读,虚读都有可能发生
- 已提交读:避免脏读。但是不可重复读和虚读都有可能发生
- 可重复读:避免脏读和不可重复读。但是虚读有可能发生
- 串行化的:避免以上所有读问题
超时时间
timeout 超时时间
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
- -1 默认值,不超时
- 以秒为单位的数值
是否只读
readOnly 是否只读
读:查询 写:添加修改删除操作
这是事务的第三个特性,是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
- false 默认值,表示可以增删改查
- true 只能查询
其他参数:
rollbackFor 回滚
设置出现哪些异常进行事务回滚
noRollbackFor 不回滚
设置出现哪些异常不进行事务回滚
完全注解开发
创建配置类
@Configuration
代表是配置类
开启组件扫描
@ComponentScan(basePackages = "com.example")
创建数据库连接池
1 |
|
创建JdbcTemplate对象
1 | //创建JdbcTemplate |
创建事务管理器对象
1 | //创建事务管理器对象 |
完整代码如下
package com.example.demo.config;
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;
@Configuration //代表是配置类
@ComponentScan(basePackages = "com.example")
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/crashcourse?serverTimezone=GMT&rewriteBatchedStatements=true");
druidDataSource.setUsername("root");
druidDataSource.setPassword("168178");
return druidDataSource;
}
//创建JdbcTemplate
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入DataSource,到IOC容器中根据类型进行注入
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器对象
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}