老面:小伙子,理解Spring的事务吗?
解读:这个必须理解,不理解间接挂~,但面试官必定不是想听你理解两个字,他是想让你简略的介绍下。
笑小枫:理解,事务在逻辑上是一组操作,要么执行,要不都不执行。次要是针对数据库而言的,比如说 MySQL。为了保障事务是正确牢靠的,在数据库进行写入或者更新操作时,就必须得体现出 ACID 的 4 个重要个性。
老面:你刚刚提到了ACID,那你简略的介绍一下这4个个性
解读:在介绍事务的时候,本能够把个性介绍了,然而偏偏不,成心一提,但不开展形容。为的就是让老面进入到咱们的节奏尽管是你面试我,但我要牵着你的鼻子走,面试就是一场心理战,这才刚刚开始~
笑小枫:ACID别离是原子性(Atomicity)、一致性(Consistency)、事务隔离(Isolation)、持久性(Durability)。
其中:原子性(Atomicity)是一个事务中的所有操作,要么全副实现,要么全副不实现,不会完结在两头某个环节。事务在执行过程中产生谬误,会被回滚(Rollback)到事务开始前的状态,就像这个事务素来没有执行过一样。
一致性(Consistency)是在事务开始之前和事务完结当前,数据库的完整性没有被毁坏。
事务隔离(Isolation)是数据库容许多个并发事务同时对其数据进行读写和批改,隔离性能够避免多个事务并发执行时因为穿插执行而导致数据的不统一。
持久性(Durability)是事务处理完结后,对数据的批改就是永恒的,即使系统故障也不会失落。
正是因为这4个个性,咱们在我的项目中会频繁的应用事务。
老面:你刚刚说,在我的项目中频繁应用事务,都是什么场景下应用的呢?
解读:又跟着节奏来了,这个比较简单
笑小枫:事务应用场景分为两种读写事务和只读事务。
咱们罕用的场景中,多表操作的时候会应用读写事务,例如A转账给B的时候,须要A扣款胜利,
B到账胜利,就要用到事务的原子性、一致性和持久性;在多表查问的时候,咱们会用到只读事务,例如A有1000元,B有2000元,这个时候总金额应该为3000元。
select amount from user where id = 'A';select amount from user where id = 'B';
如果查问完A后取到了1000元,而后A转账给B 1000元,如果没有事务,查问到B为3000元,这个时候总额就是4000元了,所以能够应用只读事务,保证数据的隔离性和一致性。
老面:你能说说具体是怎么应用的吗?
笑小枫:事务应用个别有管理机制,一种是编程式事务,就是在代码中手动的治理事务的提交、回滚等操作,代码侵入性比拟强,咱们很少应用;另外一种是申明式事务,应用@Transactional
注解,它是基于AOP面向切面的,将具体业务与事务处理局部解耦,代码侵入性很低,咱们我的项目中应用这种形式比拟多。
老面:你刚刚提到了Transactional,你晓得它有几种作用域吗?
笑小枫:能够作用在类、办法、接口上;
当把注解放在类上时,示意所有该类的public办法都配置雷同的事务属性信息。
当类配置了@Transactional,办法也配置了@Transactional,办法的事务会笼罩类的事务配置信息。
不举荐作用在接口上,因为这只有在应用基于接口的代理时它才会失效,一旦配置了Spring AOP应用CGLib动静代理,将会导致@Transactional注解生效。
老面:除了作用在类上时,Spring AOP应用CGLib动静代理,@Transactional会生效外,你还晓得其它的场景会导致事务生效吗?
解读:这还不简略,这不就是我给你挖的坑嘛列举几种状况吧,抽几条说就行,老面必定会觉着你这小子根底不错
笑小枫:
@Transactional 利用在非 public 润饰的办法上
如果Transactional注解利用在非public 润饰的办法上,Transactional将会生效。
起因是:TransactionInterceptor (事务拦截器)应用AOP,在指标办法执行前后进行拦挡,会查看指标办法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。(spring要求被代理办法必须是public的。)
- @Transactional 注解属性 propagation 设置谬误
这种是因为propagation的参数设置谬误,依据需要抉择适合的@Transactional的propagation属性的值 @Transactional 注解属性 rollbackFor 设置谬误
rollbackFor默认是error和RuntimeException会触发回滚,其余异样或自定义异样不会回滚,须要回滚则须要指定或者间接指定@Transactional(rollbackFor=Exception.class)
同一个类中办法调用,导致@Transactional生效
这个和redis缓存生效场景之一一样,如果在一个类中,B办法加了事务,A办法没加事务调用B办法,另外一个类调用A办法,则B办法的事务生效。这种状况能够加一层Mamager层对Service层通用能力的下沉。
异样被捕捉,导致@Transactional生效
当咱们是用catch把异样捕捉了,那么该办法就不会抛出异样了,那么呈现该异样就不会回滚了
数据库引擎不反对事务
这种状况呈现的概率并不高,事务是否失效数据库引擎是否反对事务是要害。罕用的MySQL数据库默认应用反对事务的innodb引擎。一旦数据库引擎切换成不反对事务的myisam,那事务就从根本上生效了。
没有被 Spring 治理
如上面例子所示:
@Servicepublic class OrderService { @Transactional public void updateOrder(Order order) { // update order }}
如果此时把 @Service 注解正文掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 治理了,事务天然就生效了。
办法被定义为final
办法被定义成了final的,这样会导致spring aop生成的代理对象不能复写该办法,而让事务生效。
老面:小伙子不错呀,你刚刚说propagation设置谬误,会导致事务生效,那你能说一下有哪几种propagation吗?
解读:伪装思考一下,让老面觉着你是一个知难不退,会思考问题的优秀人才,但不要太久,省的让老面
笑小枫:propagation是用来配置Spring事务的流传机制,共有7种流传机制,申明式事务的流传行为能够通过 @Transactional 注解中的 propagation 属性来定义,默认是Propagation.REQUIRED。
Propagation的事务流传机制如下:
- REQUIRED:如果以后存在事务,则退出该事务,如果以后不存在事务,则创立一个新的事务。
- SUPPORTS:如果以后存在事务,则退出该事务;如果以后不存在事务,则以非事务的形式持续运行。
- MANDATORY:如果以后存在事务,则退出该事务;如果以后不存在事务,则抛出异样。
- REQUIRES_NEW:从新创立一个新的事务,如果以后存在事务,暂停以后的事务。
- NOT_SUPPORTED:以非事务的形式运行,如果以后存在事务,暂停以后的事务。
- NEVER:以非事务的形式运行,如果以后存在事务,则抛出异样。
- NESTED :示意如果以后曾经存在一个事务,那么该办法将会在嵌套事务中运行。嵌套的事务能够独立于以后事务进行独自地提交或回滚。如果以后事务不存在,那么其行为和 Propagation.REQUIRED 成果一样。
老面:除了propagation,你还晓得 @Transactional 其余的属性吗?
笑小枫:咱们之前曾经说了一些:
- propagation:可选的事务流传行为设置
- readonly:读写或只读事务,默认读写
- rollbackFor:导致事务回滚的异样类数组
除此之外@Transactional 还有以下的属性:
- value:可选的限定描述符,指定应用的事务管理器
- isolation:可选的事务隔离级别设置
- timeout:事务超时工夫设置
- rollbackForClassName:导致事务回滚的异样类名字数组
- noRollbackFor:不会导致事务回滚的异样类数组
- noRollbackForClassName:不会导致事务回滚的异样类名字数组
老面:小伙子不错嘛,那你再简略的说说事务有哪几种隔离级别
解读:一个Spring事务你能问这么多,问完这个该完结了吧,我快扛不住了~
笑小枫:事务的隔离级别是通过Isolation属性管制的,共有5个属性,其中DEFAULT为默认值,代表应用底层数据库默认的隔离级别,其余的四个对应着数据库的4种隔离级别。别离是:
- DEFAULT:应用底层数据库默认的隔离级别。 MySQL的默认隔离级别:REPEATABLE_READ(可反复读);Oracle默认的隔离级别:READ_COMMITTED(读已提交)
- READ_UNCOMMITTED:读未提交(存在脏读、不可反复读、幻读)根本不应用
- READ_COMMITTED:读已提交(解决度脏数据,存在不可反复读与幻读)
- REPEATABLE_READ:可反复读(解决脏读、不可反复读,存在幻读)
- SERIALIZABLE:串行化 。因为事务是串行执行,所以效率会大大降落,应用程序的性能会急剧升高。如果没有特地重要的情景,个别都不会应用 Serializable 隔离级别。
应用办法:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
老面:既然提到了 脏读、不可反复读、幻读,那你解释下别离是什么意思吧
解读:这个还好,千万别问原理问原理我也不会~
笑小枫:
脏读(dirty reads),事务能够看到其余事务“尚未提交”的批改。如果另一个事务回滚,那么以后事务读到的数据就是脏数据。
不可反复读(Non Repeatable Read),在一个事务内,屡次读同一数据,在这个事务还没有完结时,如果另一个事务恰好批改了这个数据,那么,在第一个事务中,两次读取的数据就可能不统一。
幻读(Phantom Read),幻读是指,在一个事务中,第一次查问某条记录,发现没有,然而,当试图更新这条不存在的记录时,居然能胜利,并且,再次读取同一条记录,它就神奇地呈现了。
老面:最初,你再说下事务实现的原理吧
解读:怕什么,来什么,巴拉巴拉巴拉,我也不会,面试到此结束,本文也到此结束啦~
当然言归正传,这个还是要本人去看源码,一步一步去了解,本人不了解,就算背会了,你背的答案也不肯定对,而且说原理的过程也很多变,原理中也用到了很多技术点,还是要多学习,多积攒,纯说原理很形象~
笑小枫:简略点说,Spring的事务实现的原理,就是通过这样一个动静代理对所有须要事务管理的Bean进行加载,并依据配置在invoke办法中对以后调用的办法名进行断定,并在method.invoke办法前后为其加上适合的事务管理代码,这样就实现了Spring式的事务管理。
具体的底层实现去这个地址看看吧~我就不班门弄斧了,必定总结不到大佬这么全面
https://www.processon.com/view/link/5fab6edf1e0853569633cc06