Mysql事务开启方式(客户端+java手动+Spring Boot)

一:概念

      做为单个逻辑单元执行一系列操做,要么彻底执行,要么彻底不执行。举例 咱们须要向数据库插入3条数据(咱们但愿这三条数据要么所有插入成功,要么所有失败), 好比第一条数据插入成功,插入第二条数据失败(显然这已经不是一个完整的业务数据),那么第三条数据也无需执行。那么咱们就能够用到事务了。java

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

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

隔离级别 事务 脏读 不可重复读 幻读
TRANSACTION_NONE 不支持 不适用 不适用 不适用
TRANSACTION_SERIALIZABLE 支持 防止 防止 防止
TRANSACTION_READ_COMMITTED(默认 支持 防止 容许 容许
TRANSACTION_REPEATABLE_READ 支持 防止 防止 容许
TRANSACTION_READ_UNCOMMITTED 支持 容许 容许 容许

 

 

 

 

 

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

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

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

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

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

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

备注2: Java设置隔离级别session

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

相关文章
相关标签/搜索