zoukankan      html  css  js  c++  java
  • Mysql事务开启方式(客户端+java手动+Spring Boot)

    一:概念

          作为单个逻辑单元执行一系列操作,要么完全执行,要么完全不执行。举例 我们需要向数据库插入3条数据(我们希望这三条数据要么全部插入成功,要么全部失败), 比如第一条数据插入成功,插入第二条数据失败(显然这已经不是一个完整的业务数据),那么第三条数据也无需执行。那么我们就可以用到事务了。

    二:事务的特性和隔离级别

          为了避免在事务期间发生冲突,DBMS使用锁定机制来阻止其他人访问事务正在访问的数据。(请注意,在自动提交模式下,每个语句都是一个事务,只保留一个语句的锁定。)设置锁定后,它将一直有效,直到提交或回滚事务为止。例如,DBMS可以锁定表的一行,直到对其进行更新为止。此锁定的作用是防止用户获取脏读,即在永久化之前读取值。(访问尚未提交的更新值被视为脏读因为该值可以回滚到其先前的值。如果您读取稍后回滚的值,则会读取无效值。)

    隔离级别 事务 脏读 不可重复读 幻读  描述
    TRANSACTION_NONE 不支持 不适用 不适用 不适用  
    TRANSACTION_SERIALIZABLE 支持 防止 防止 防止  
    TRANSACTION_READ_COMMITTED 支持 防止 允许 允许 在同一个事务中查询,不能确保每次查询的数据相同,即可以查询到另一个事务更新后的数据(与repeatable_read形成鲜明对比)。
    TRANSACTION_REPEATABLE_READ(默认) 支持 防止 防止 允许 在同一个事务中的查询,可以确保每次读取的数据相同。即使另一个事务提交了更新也读不到更新后的数据。
    TRANSACTION_READ_UNCOMMITTED 支持 允许 允许 允许 在同一个事务中的查询,可以查询到另一个事务没有提交的数据

    脏读   :   读取了另一个事务没有提交的数据,默认隔离级别,即防止了别人读取到我们没有提交(commit)的数据

    重复读:对一个开启了事务连接,在第一次查询一行数据(此次另一个开启的事务更新了这一条数据),与第二次查询的数据不一样。即两次查询同一条数据不一样

    幻读   :对一个开启了事务连接,第一次查询的数据行数(此次另一个开启的事务的连接新增了一条),与第二次查询的行数不一样(好像产生了幻觉)。即两次查询的数量不一样

    备注:事务隔离级别

    备注:因在官方文档中没有找到相关的名词解释(幻读,不可重复读),故在此废弃。

    备注:个人理解脏读,幻读,重复读可以上我们看到数据的整个变化过程,而不是只注重结果,故我认为这并不算是一种bug。打个比方说我种了了一亩地的西瓜,在我准备第二天收获之前我先去数了一下共有200个大西瓜。等到第二天我去收获的时候我只收获了198个,还有两个去哪里了呢?我猜测可能是被那个口渴的路人偷吃了吧,我不会纠结这两个西瓜到哪里去了。当然这也要根据业务场景分析了,如果说你第一天数的200个西瓜并兴高采烈的告诉了你的老婆大人(多线程查询同一条数据),然而收获回去只有198个,那你就要和她解释了。如果她是个通情达理的人这就不是bug,如果不是你就把它认为bug吧。

    备注1:客户端设置隔离级别

    mysql>  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    Query OK, 0 rows affected (0.00 sec)

    备注2: Java设置隔离级别

    Connection.getTransactionIsolation//获取当前隔离级别
    Connection.setTransactionIsolation)//设置当前隔离级别

    注意:一次事务只能设置一次隔离即便

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    ERROR 1568 (25001): Transaction characteristics can't be changed while a transaction is in progress

    三:客户端操作方法

      备注:默认数据库事务是关闭的,即执行更新(修改)表的语句,MySQL就会将更新存储在磁盘上以使其永久化。无法回滚更改。

                      我们可以通过执行  SET autocommit=0; 命令设置事务开启状态,即每条更新语句都需要手动commit提交事务(只对当前session有效,即其他客户端更新操作是没有事务)

    • START TRANSACTION或 BEGIN开始新的交易。

    • COMMIT 提交当前事务,使其更改永久化。

    • ROLLBACK 回滚当前事务,取消其更改。

    • SET autocommit 禁用或启用当前会话的默认自动提交模式。

    四:java手动控制

     即:手动控制事务的开启和关闭

    public class RawTransactions {
        
        private final JdbcTemplate jdbcTemplate;
    
        public RawTransactions(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        } 
        public void book(String... persons) {
            //开启事务
            jdbcTemplate.execute("START TRANSACTION");
            for (String person : persons) {
                try {
                     jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
                } catch (RuntimeException e) {
                    System.out.println("----发生异常数据回滚 -----");
                    jdbcTemplate.execute("ROLLBACK");
                    break;
                }  
            }
            //提交事务
            jdbcTemplate.execute("COMMIT");
        }
        public List<String> findAllBookings() {
            return jdbcTemplate.query("select FIRST_NAME from BOOKINGS",
                    (rs, rowNum) -> rs.getString("FIRST_NAME"));
        }
    
    }

    项目地址:https://github.com/374003909/JdbcTransactions/blob/master/src/main/java/hello/RawTransactions.java

    五:spring boot开启事务

    即:在spring boot框架中通过注解@Transactional 实现

    @Component
    public class BookingService {
    
        private final static Logger logger = LoggerFactory.getLogger(BookingService.class);
    
        private final JdbcTemplate jdbcTemplate;
    
        public BookingService(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        @Transactional
        public void book(String... persons) {
            for (String person : persons) {
                logger.info("Booking " + person + " in a seat...");
                jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
            }
        }
    
        public List<String> findAllBookings() {
            return jdbcTemplate.query("select FIRST_NAME from BOOKINGS",
                    (rs, rowNum) -> rs.getString("FIRST_NAME"));
        }
    
    }

    项目地址:https://github.com/374003909/JdbcTransactions/blob/master/src/main/java/hello/BookingService.java

    测试入口:https://github.com/374003909/JdbcTransactions/blob/master/src/main/java/hello/AppRunner.java

    项目地址:https://github.com/374003909/JdbcTransactions

  • 相关阅读:
    BZOJ 4802 欧拉函数(Pollard_Rho)
    Codeforces 804E The same permutation(构造)
    Codeforces 804D Expected diameter of a tree(树形DP+期望)
    BZOJ 3399 [Usaco2009 Mar]Sand Castle城堡(贪心)
    BZOJ 2430 [Poi2003]Chocolate(贪心+归并排序)
    BZOJ 1707 [Usaco2007 Nov]tanning分配防晒霜(扫描线+贪心+优先队列)
    BZOJ 1828 [Usaco2010 Mar]balloc 农场分配(贪心+线段树)
    BZOJ 1827 [Usaco2010 Mar]gather 奶牛大集会(树形DP)
    BZOJ 2697 特技飞行(贪心)
    BZOJ 4883 [Lydsy2017年5月月赛]棋盘上的守卫(最小生成环套树森林)
  • 原文地址:https://www.cnblogs.com/jinliang374003909/p/10489528.html
Copyright © 2011-2022 走看看