1.1 事务的概念
1.1.1什么事务? 事务是一些sql序列的集合, 是多条sql, 作为一个整体执行。
事务(Transaction)是数据库管理系统(DBMS)中的一个操作单元或工作单元,它由一组数据库操作组成,这些操作要么全部成功执行,要么全部失败,保证了数据库的一致性和完整性。
mysql执行事务 beginTransaction 开启事务 insert into student() values..... select * from student where id=1001 update school set name=xxx where id=1005 endTransaction 事务结束
1.1.2 什么情况下需要使用事务?
一个操作需要多条(2条或2条以上的sql)sql语句一起完成, 操作才能成功。
举个例子: 当你遇到一个你要实现的功能逻辑的时候 涉及到 先要修改再去添加 等多重操作的时候 以免 一个sql执行遇到问题 直接抛出异常 没有执行到下一条sql 此时可能会对系统造成难以排查的错误 所以我们需要用到事务的回滚; rollback
1.1.3 当面试官询问事务时,你可以这样回答:
"事务是数据库中执行的一个操作单元,它由一系列数据库操作组成,这些操作作为一个不可分割的整体执行。事务具有 ACID 属性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。原子性确保事务中的操作要么全部成功执行,要么全部失败回滚,不会留下部分执行的情况。一致性保证了事务在执行前后数据库从一个一致性状态转换到另一个一致性状态。隔离性确保多个事务之间相互隔离,避免相互干扰。持久性指的是事务一旦提交,其对数据库所做的修改将永久保存。"
当谈论事务和幻读时,MySQL的隔离级别起着重要作用。幻读是指在一个事务执行期间,如果另一个事务插入了新的行或者删除了现有行,第一个事务的查询结果集可能会发生变化,就好像产生了幻觉一样。幻读是在可重复读隔离级别下出现的一种现象,即使在这个隔离级别下,数据库仍然无法完全消除并发事务可能产生的幻读。
从解决幻读的角度来说,可以谈论数据库的隔离级别和锁机制。在MySQL中,有多个隔离级别,例如读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。不同的隔离级别决定了事务在并发执行时的可见性和隔离程度。
针对幻读问题,MySQL的隔离级别为可重复读(Repeatable Read)提供了部分解决方案,但仍无法完全解决。在这个隔离级别下,MySQL通过多版本并发控制(MVCC)来提供一致性读取,通过在读取数据时创建快照来避免读取到其他事务的未提交数据。尽管这可以解决某些幻读问题,但并不能完全消除。
此外,谈及数据库锁机制也很重要。MySQL使用行级锁和表级锁来协调并发访问。对于幻读问题,行级锁和间隙锁有助于控制并发事务的访问,减少幻读的发生。
在面试时,你可以从数据库的隔离级别、并发控制机制、锁机制以及数据库如何处理并发事务等方面,讨论MySQL无法完全解决幻读的问题。
1.2 在程序中事务在哪说明
事务:加在业务类的方法上面(public方法上面),表示业务方法执行时,需要事务的支持。
public class AccountService{
private AccountDao dao;
private MoneyDao dao2;
// 在service(业务类)的public方法上面,需要说明事务。
public void trans(String a, String b, Integer money){
dao.updateA();
dao.updateB();
dao2.insertA();
}
}
public class AccountDao{
public void updateA(){}
public void updateB(){}
}
public class MoneyDao{
public void insertA(){}
public void deleteB(){}
}
1.3 事务管理器
1.3.1 不同的数据库访问技术,处理事务是不同的
1)使用jdbc访问数据库, 事务处理。
public void updateAccount(){
Connection conn = ...
conn.setAutoCommit(false);
stat.insert()
stat.update();
conn.commit();
con.setAutoCommit(true)
}
2) mybatis执行数据库,处理事务
public void updateAccount(){
SqlSession session = SqlSession.openSession(false);
try{
session.insert("insert into student...");
session.update("update school ...");
session.commit();
}catch(Exception e){
session.rollback();
}
}
1.3. 2 spring统一管理事务, 把不同的数据库访问技术的事务处理统一起来。
使用spring的事务管理器,管理不同数据库访问技术的事务处理。 开发人员只需要掌握spring的事务处理一个方案, 就可以实现使用不同数据库访问技术的事务管理。
管理事务面向的是spring, 有spring管理事务,做事务提交,事务回顾。
1.3.3 Spring事务管理器
Spring框架使用事务管理器对象,管理所有的事务。
事务管理器接口: PlatformTransactionManager
作用: 定义了事务的操作, 主要是 commit(),
rollback()
事务管理器有很多实现类: 一种数据库的访问技术有一个实现类。
由实现类具体完成事务的提交,回顾。
意味着: jdbc或者mybatis访问数据库有自己的事务管理器实现类 : DataSourceTranactionManager
hibernate框架,他的事务管理器实现类: HibernateTransactionManager.
事务管理器工作方式:
1.3.4 事务的提交和回顾的时机
什么时候提交事务,回滚事务 ?
当你的业务方法正常执行时,没有异常,事务是提交的。 如果你的业务方法抛出了运行时异常, 事务是回滚的。
异常分类:
Error: 严重错误。 回滚事务。 编译时
Exception : 异常类,可以出来的异常情况
1) 运行时异常: RuntimeException和他的子类都是运行时异常, 在程序执行过程中抛出的异常。 常见的运行时异常: NullPoinerException , NumberFormatException , ArithmeticException, IndexOutOfBoundsException.
2) 受查异常 : 编写java代码的时候,必须出来的异常。 例如IOException , SQLException , FileNotFoundException
怎么记忆:
方法中抛出了运行时异常, 事务回滚,其他情况(正常执行方法,受查异常)就是提交事务。
1.3.5 事务使用的AOP的环绕通知
环绕通知:可以在目标方法的前和后都能增强功能,不需要修改代码代码
spring给业务方法在执行时,增加上事务的切面功能
@Around("execution(* 所有的业务类中的方法)")
public Object myAround(ProceedingJoinPoint pjp) {
try{
PlatformTransactionManager.beginTransaction();//使用spring的事务管理器,开启事务
pjp.proceed(); //执行目标方法 //doSome()
PlatformTransactionManager.commit();//业务方法正常执行,提交事务
}catch(Exception e){
PlatformTransactionManager.rollback();//业务方法正常执行,回滚事务
}
}
1.4 事务定义接口TransactionDefinition
TransactionDefinition接口。定义了三类常量, 定义了有关事务控制的属性。
事务的属性:1) 隔离级别 2)传播行为 3)事务的超时
给业务方法说明事务属性。和ACID不一样。
ACID :事务ACID理解_dengjili的博客-CSDN博客_acid
1.4.1 隔离级别
隔离级别:控制事务之间影响的程度。
5个值,只有四个隔离级别
1)DEFAULT : 采 用 DB 默 认 的 事 务 隔 离 级 别 。 MySql 的 默 认 为 REPEATABLE_READ;
Oracle 默认为 READ_COMMITTED。
2)READ_UNCOMMITTED:读未提交。未解决任何并发问题。 3)READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。 4)REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读 5)SERIALIZABLE:串行化。不存在并发问题。
1.4.2 超时时间
超时时间,以秒为单位 。 整数值。 默认是 -1
超时时间:表示一个业务方法最长的执行时间,没有到达时间没有执行完毕,spring回滚事务。
1.4.3 传播行为
传播行为有7个值。
传播行为:业务方法在调用时,事务在方法之间的,传递和使用。
使用传播行为,标识方法有无事务。
PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATION_SUPPORTS
以上三个需要掌握的。
PROPAGATION_MANDATORY PROPAGATION_NESTED PROPAGATION_NEVER PROPAGATION_NOT_SUPPORTED
1) REQUIRED: spring默认传播行为, 方法在调用的时候,如果存在事务就是使用当前的事务,如果没有事务,则新建事务, 方法在新事务中执行。
2) SUPPORTS:支持, 方法有事务可以正常执行,没有事务也可以正常执行。
3)REQUIRES_NEW:方法需要一个新事务。 如果调用方法时,存在一个事务,则原来的事务暂停。 直到新事务执行完毕。 如果方法调用时,没有事务,则新建一个事务,在新事务执行代码。
1.5 Spring框架使用自己的注解@Transactional控制事务
@Transactional注解, 使用注解的属性控制事务(隔离级别,传播行为,超时)
属性:
1. propagation : 事务的传播行为, 他使用的 Propagation类的枚举值。例如 Propagation.REQUIRED
2.isolation : 表示隔离级别, 使用Isolation类的枚举值,表示隔离级别。 默认 Isolation.DEFAULT
3.readOnly: boolean类型的值,表示数据库操作是不是只读的。默认是false
4.timeout: 事务超时,默认是-1, 整数值,单位是秒。 例如 timeout=20
5.rollbackFor:表示回滚的异常类列表, 他的值是一个数组,每个值是异常类型的class。
6.rollbackForClassName:表示回滚的异常类列表,他的值是异常类名称,是String类型的值
7.noRollbackFor: 不需要回滚的异常类列表。 是class类型的。
8.noRollbackForClassName: 不需要回滚的异常类列表,是String类型的值
位置:1)在业务方法的上面, 是在public方法的上面
2) 在类的上面。
注解的使用步骤:
1)在spring的配置文件,声明事务的内容
声明事务管理器,说明使用哪个事务管理器对象
声明使用注解管理事务, 开启是注解驱动
2)在类的源代码中,加入@Transactional.
事务的控制模式: 1. 编程式,在代码中编程控制事务。
2. 声明式事务。不用编码
例子:
spring配置文件
<!--声明事务的控制-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源DataSource-->
<property name="dataSource" ref="myDataSource" />
</bean>
<!--开启事务注解驱动: 告诉框架使用注解管理事务
transaction-manager:指定事务管理器的id
-->
<tx:annotation-driven transaction-manager="transactionManager" />
java代码
//@Transactional 放在public方法的上面。表示方法有事务功能
/*
第一种设置方式
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false, timeout = 20,
rollbackFor = {NullPointerException.class,NotEnougthException.class})
*/
/*
第二种设置方式
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false, timeout = 20
)
解释 rollbackFor 的使用;
1)框架首先检查方法抛出的异常是不是在 rollbackFor 的数组中, 如果在一定回滚。
2)如果方法抛出的异步不在 rollbackFor 数组, 框架会继续检查 抛出的异常 是不是 RuntimeException.
如果是RuntimeException, 一定回滚。
例如 抛出 SQLException , IOException
rollbackFor={SQLException.class, IOException.class}
*/
//第三种方式: 使用默认值 REQUIRED , 发生运行时异常回滚。
@Transactional
@Override
public void buy(Integer goodsId, Integer num) { }
@Transactional使用的特点:
1.spring框架自己提供的事务控制
2.适合中小型项目。
3.使用方便,效率高。
1.6 使用Aspectj框架在spring的配置文件中,声明事务控制
使用aspectj的aop,声明事务控制叫做声明式事务
使用步骤:
pom.xml加入 spring-aspects的依赖
在spring的配置文件声明事务的内容
1)声明事务管理器
2)声明业务方法需要的事务属性
3)声明切入点表达式
声明式事务
<!--声明式事务: 不用写代码 -->
<!--1. 声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource" />
</bean>
<!--2.声明业务方法的事务属性(隔离级别,传播行为,超时)
id:给业务方法配置事务段代码起个名称,唯一值
transaction-manager:事务管理器的id
-->
<tx:advice id="serviceAdvice" transaction-manager="transactionManager">
<!--给具体的业务方法增加事务的说明-->
<tx:attributes>
<!--
给具体的业务方法,说明他需要的事务属性
name: 业务方法名称。
配置name的值: 1. 业务方法的名称; 2. 带有部分通配符(*)的方法名称; 3 使用*
propagation:指定传播行为的值
isolation:隔离级别
read-only:是否只读,默认是false
timeout:超时时间
rollback-for:指定回滚的异常类列表,使用的异常全限定名称
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
read-only="false" timeout="20"
rollback-for="java.lang.NullPointerException,com.bjpowernode.excetion.NotEnougthException"/>
<!--在业务方法有命名规则后, 可以对一些方法使用事务-->
<tx:method name="add*" propagation="REQUIRES_NEW"
rollback-for="java.lang.Exception" />
<tx:method name="modify*"
propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="remove*"
propagation="REQUIRED" rollback-for="java.lang.Exception" />
<!--以上方法以外的 * :querySale, findSale, searchSale -->
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!--声明切入点表达式: 表示那些包中的类,类中的方法参与事务-->
<aop:config>
<!--声明切入点表达式
expression:切入点表达式, 表示那些类和类中的方法要参与事务
id:切入点表达式的名称,唯一值
expression怎么写?
-->
<aop:pointcut id="servicePointcut" expression="execution(* *..service..*.*(..))" />
<!--关联切入点表达式和事务通知-->
<aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointcut" />
</aop:config>
声明式事务优缺点:
缺点: 理解难, 配置复杂。
优点: 代码和事务配置是分开的。 控制事务源代码不用修改。
能快速的了解和掌控项目的全部事务。 适合大型项目。