Spring事务和MySQL事务详解面试

Spring事务和MySQL事务详解面试文章目录数据库事务事务是什么事务的四大特性 MySQL 事务隔离级别查看 MySQL 当前事务隔离级别 MySQL 默认操作模式为自动提交模式 JDBC 处理事务 Spring 事务 Spring 的事务传播 PROPAGATION REQUIREDPROP SUPPORTSPROP MANDATORYPRO REQUIRES NEWPROPAGATI NOT SUPPORTEDPRO NEVERPROPAGA NESTEDSpring 事务的隔离级别 Spring

数据库事务

事务是什么

是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合。

事务的四大特性

  1. 原子性
    事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做

  2. 一致性
    事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。

  3. 隔离性
    一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

  4. 持续性
    也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。

MySQL事务隔离级别

隔离级别 隔离级别的值 导致的问题
Read-Uncommitted 0 导致脏读
Read-Committed 1 避免脏读,允许不可重复读和幻读
Repeatable-Read 2 MySQL默认的隔离级别。避免脏读,不可重复读,允许幻读
Serializable 3 串行化读,事务只能一个一个执行,避免了 脏读、不可重复读、幻读。执行效率慢,使用时慎重

1. 脏读

一个事务对数据进行了增删改查,但是未提交事务。另一个事物可以读取到未提交的数据,如果第一个事务进行了回滚,那么第二个事务就读到了脏数据。

例子:

领导给张三发工资,10000元已打到张三账户,但该事务还未提交,正好这时候张三去查询工资,发现10000元已到账。这时领导发现张三工资算多了5000元,于是回滚了事务,修改了金额后将事务提交。最后张三实际到账的只有5000元。

2. 不可重复度

一次事务发生了两次读操作,两个读操作之间发生了另一个事务对数据修改操作,这时候第一次和第二次读到的数据不一致。

不可重复度关注点在数据更新和删除,通过行级锁可以实现可重复读的隔离级别。

例子:

张三需要转正1000元,系统读到卡余额有2000元,此时张三老婆正好需要转正2000元,并且在张三提交事务前把2000元转走了,当张三提交转账是系统提示余额不足。

3. 幻读

幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。

相对于不可重复读,幻读更关注其它事务的新增数据。通过行级锁可以避免不可重复读,但无法解决幻读的问题,想要解决幻读,只能通过Serializable隔离级别来实现。

例子:

张三老婆准备打印张三这个月的信用卡消费记录,经查询发现消费了两次共1000元,而这时张三刚按摩完准备结账,消费了1000元,这时银行记录新增了一条1000元的消费记录。当张三老婆将消费记录打印出来时,发现总额变为了2000元,这让张三老婆很诧异。

4. 串行化读

Serializable是最高的隔离级别,性能很低,一般很少用。在这级别下,事务是串行顺序执行的,不仅避免了脏读、不可重复读,还避免了幻读。

查看MySQL当前事务隔离级别

MySQL InnoDB默认的事务隔离级别为REPEATABLE-READ

mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 

MySQL默认操作模式为自动提交模式

除非显示的开启一个事务,否则每个查询都被当成一个单独的事务自动执行。可以通脱设置autocommit的值改变默认的提交模式。

  1. 查看当前提交模式
mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 
  1. 关闭自动提交。0代表关闭,1代表开启。
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | OFF | +---------------+-------+ 

JDBC处理事务

Connection connection = null; PreparedStatement pstmt = null; ResultSet resultSet = null; try { 
    Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbname?characterEncoding=utf-8","username", "password"); connection.setAutoCommit(false); // others ...... connection.commit(); } catch (Exception e) { 
    connection.rollback(); } finally { 
    connection.setAutoCommit(true); // close connection } 

Spring事务

Spring事务本质是对数据库事务的支持,如果数据库不支持事务(例如MySQL的MyISAM引擎不支持事务),则Spring事务也不会生效。

Spring的事务传播

事务传播行为是指一个事务方法A被另一个事务方法B调用时,这个事务A应该如何处理。事务A应该在事务B中运行还是另起一个事务,这个有事务A的传播行为决定。

事务传播属性定义TransactionDefinition

int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6; 
常量名称 常量解释
PROPAGATION_REQUIRED 支持当前事务,如果当前没有事务,就新建一个事务。这是Spring 默认的事务的传播。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚之后, 不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获, 也可以不处理回滚操作。 使用JtaTransactionManager作为事务管理器
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。使用JtaTransactionManager作为事务管理器
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

PROPAGATION_REQUIRED

如下例子,单独调用methodB时,当前上下文没有事务,所以会开启一个新的事务。

调用methodA方法时,因为当前上下文不存在事务,所以会开启一个新的事务。当执行到methodB时,methodB发现当前上下文有事务,因此就加入到当前事务A中来。

@Transactional(propagation = Propagation.REQUIRED) public void methodA() { 
    methodB(); // do something } @Transactional(propagation = Propagation.REQUIRED) public void methodB() { 
    // do something } 

PROPAGATION_SUPPORTS

单独的调用methodB时,methodB方法是非事务的执行的。当调用methdA时,methodB则加入了methodA的事务中,事务地执行。

@Transactional(propagation = Propagation.REQUIRED) public void methodA() { 
    methodB(); // do something } // 事务属性为SUPPORTS @Transactional(propagation = Propagation.SUPPORTS) public void methodB() { 
    // do something } 

PROPAGATION_MANDATORY

当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”)

当调用methodA时,methodB则加入到methodA的事务中,以事务方式执行。

@Transactional(propagation = Propagation.REQUIRED) public void methodA() { 
    methodB(); // do something } @Transactional(propagation = Propagation.MANDATORY) public void methodB() { 
    // do something } 

PROPAGATION_REQUIRES_NEW

从下面代码可以看出,事务B与事务A是两个独立的事务,互不相干。事务B是否成功并不依赖于 事务A。如果methodA方法在调用methodB方法后的doSomeThingB方法失败了,而methodB方法所做的结果依然被提交。而除了 methodB之外的其它代码导致的结果却被回滚了

@Transactional(propagation = Propagation.REQUIRED) public void methodA() { 
    doSomeThingA(); methodB(); doSomeThingB(); // do something else } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { 
    // do something } 

当调用methodA(),相当于

public static void main(){ 
    TransactionManager tm = null; try{ 
    //获得一个JTA事务管理器 tm = getTransactionManager(); tm.begin();//开启一个新的事务 Transaction ts1 = tm.getTransaction(); doSomeThing(); tm.suspend();//挂起当前事务 try{ 
    tm.begin();//重新开启第二个事务 Transaction ts2 = tm.getTransaction(); methodB(); ts2.commit();//提交第二个事务 } Catch(RunTimeException ex) { 
    ts2.rollback();//回滚第二个事务 } finally { 
    //释放资源 } //methodB执行完后,恢复第一个事务 tm.resume(ts1); doSomeThingB(); ts1.commit();//提交第一个事务 } catch(RunTimeException ex) { 
    ts1.rollback();//回滚第一个事务 } finally { 
    //释放资源 } } 

PROPAGATION_NOT_SUPPORTED

总是非事务地执行,并挂起任何存在的事务。

使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。

image

PROPAGATION_NEVER

总是非事务地执行,如果存在一个活动事务,则抛出异常。

PROPAGATION_NESTED

如果一个活动的事务存在,则运行在一个嵌套的事务中。

如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。

这是一个嵌套事务,使用JDBC3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。 需要JDBC 驱动的java.sql.Savepoint类。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true(属性值默认为false)。

image

@Transactional(propagation = Propagation.REQUIRED) methodA(){ 
    doSomeThingA(); methodB(); doSomeThingB(); } @Transactional(propagation = Propagation.NEWSTED) methodB(){ 
    // do something } 

单独调用methodB方法,则按REQUIRED属性执行。如果调用methodA方法,则相当于:

main(){ 
    Connection con = null; Savepoint savepoint = null; try{ 
    con = getConnection(); con.setAutoCommit(false); doSomeThingA(); savepoint = con2.setSavepoint(); try{ 
    methodB(); } catch(RuntimeException ex) { 
    con.rollback(savepoint); } finally { 
    //释放资源 } doSomeThingB(); con.commit(); } catch(RuntimeException ex) { 
    con.rollback(); } finally { 
    //释放资源 } } 

当methodB方法调用之前,调用setSavepoint方法,保存当前的状态到savepoint。如果methodB方法调用失败,则恢复到之前保存的状态。

需要注意的是,这时的事务并没有进行提交,如果后续的代码(doSomeThingB()方法)调用失败,则回滚包括methodB方法的所有操作。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

Spring事务的隔离级别

事务隔离级别定义TransactionDefinition

int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2; int ISOLATION_REPEATABLE_READ = 4; int ISOLATION_SERIALIZABLE = 8; 
隔离级别 解释
ISOLATION_DEFAULT 这是个 PlatfromTransactionManager 默认的隔离级别, 使用数据库默认的事务隔离级别。另外四个与 JDBC 的 隔离级别相对应。
ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它允许另外一个事务可以看 到这个事务未提交的数据。这种隔离级别会产生脏读, 不可重复读和幻像读。
ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读 取。另外一个事务不能读取该事务未提交的数据。 ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被 处理为顺序执行。

Spring事务基本配置样例

<aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/>  
     bean> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/> <tx:method name="remove*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/> <tx:method name="edit*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/> <tx:method name="login" propagation="NOT_SUPPORTED"/> <tx:method name="query*" read-only="true"/>  
      tx:attributes>  
       tx:advice> <aop:config> <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut"/> <aop:aspect ref="dataSource"> <aop:pointcut id="transactionPointcut" expression="execution(public * com.gupaoedu..*.service..*Service.*(..))" />  
        aop:aspect>  
         aop:config> 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/177272.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午7:54
下一篇 2026年3月26日 下午7:54


相关推荐

  • 会计初级学习笔记

    会计初级学习笔记

    2021年6月30日
    89
  • 2020-10-24

    2020-10-24产品经理面试习题大汇总凡事“预则立,不预则费”。即使你有丰富的产品经验,在面试那种紧张的环境下要面试好也不是一件易事,因为在那种环境下,你要对面试官提出的问题快速反映,快速组织语言,而你又没有经常训练这种能力,想回答好还是很不容易的,如果你经常背一些产品经理的面试题,那你回答的时候就流畅多了,下面将一些常见的产品经理面试题整理下来,需要的小伙伴拿去。1、介绍一下你自己介绍一下自己的姓名,年龄、毕业院校,工作经历。简单的介绍,保持在三分钟以内,给面试官问问题的时间。工作经历主要讲一些.

    2022年6月20日
    22
  • telnet如何连接_telnet使用方法

    telnet如何连接_telnet使用方法目录准备的东西开启服务连接准备的东西两台主机这里设主机A和主机B用主机A访问主机B开启服务两台电脑都要开telnet服务这时候连接你会发现连不上,提示23端口未打开。看一下,确实没有打开23端口。启动之后23端口就打开了。如下图所示连接连接过程中,会提示这个错误。是因为我们用A访问B时,用的B主机上面的账户。但是B主机的账户并没有在TelnetClicents这个组里。在B主机里把账户添加到这个组就可以连接了。…

    2025年11月21日
    4
  • ubuntu更改root用户密码

    ubuntu更改root用户密码如果要在 ubuntu 的当前用户模式下更改 root 用户的密码 可以直接打开命令行窗口输入 sudopasswd nbsp 回车 然后会提示输入 UNIX 密码 并再次确认前面输入的 UNIX 密码 两次密码输入完成后 在命令行中输入 reboot 重启系统 然后再进入系统的时候就可以在命令行中使用 su root 回车 然后输入刚才设置的密码就可以进入 root 模式下了

    2026年3月16日
    3
  • 现货黄金贵金属投资入门基础知识

    现货黄金贵金属投资入门基础知识现货黄金贵金属投资入门知识:  很多中小投资者对证券投资都比较熟悉,但对现货黄金贵金属投资还不是很熟悉。现货吧给大家做一些简单的介绍。希望能为大家进入现货黄金贵金属投资的市场提供一些有益的参考和帮助。  现货黄金.jpg  黄金贵金属投资分为实物黄金和合约式黄金,投资实物黄金的可靠性较强,但买卖过程比较麻烦,而且还要自己承担保管的成本和风险,投资起来明显不如合约

    2022年5月8日
    28
  • Boost.Lockfree官方文档[通俗易懂]

    Boost.Lockfree官方文档[通俗易懂]目录介绍与动机简介与术语非阻塞数据结构的性质非阻塞数据结构的性能阻塞行为的来源数据结构数据结构配置示例队列栈无等待单生产者/单消费者队列脚注介绍与动机简介与术语术语“非阻塞”表示并发数据结构,该结构不使用传统的同步原语(例如警卫程序)来确保线程安全。MauriceHerlihy和NirShavit(比较“多处理器编程的艺术”)区分了3种类型的非阻塞数据结构,每种结构具有不同的属性:如果保证每个并发操作都可以在有限的步骤中完成,则数据.

    2022年7月19日
    28

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号