MySQL事务(transaction)之手动控制sql事务及JDBC事务的开启

1.概念

事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。

2.管理事务

2.1数据库默认的事务

数据库默认支持事务,默认的事务是:一条sql一个事务。

2.2手动控制事务

sql控制事务

开启事务:start transaction;

开启事务后,在这条语句之后的所有sql都将处在同一个事务中

提交事务:commit;

从开启事务到提交事务之间的所有sql一起执行

回滚事务:rollback;

从开启事务到回滚事务之间的所有sql一起失效

sql脚本示例如下:

说明:当开启事务START TRANSACTION;后执行给a用户扣款100,给b用户加钱100,如果commit,那么a被扣除100,b则增加100。如果执行rollback,那么a和b的金额就会保持不变。

#slq控制事务,
#开启事务
START TRANSACTION;
UPDATE myuser SET money= money-100 WHERE name='a';
UPDATE myuser SET money= money+100 WHERE name='b';
COMMIT;
#回滚会取消开启到结束的所有sql
ROLLBACK;
#查看修改后的数据,验证事务
SELECT* FROM myuser;

JDBC控制事务

开启事务:conn.setAutoCommit(false);

jdbc中sql会自动提交,如果设置sql不自动提交,就需要我们手动提交,即提交前的所有sql在一个事务中

提交事务:conn.commit();

回滚事务:conn.rollback();

示例代码如下:

说明:测试类TransactionDemo1用于测试JDBC的事务控制,其中会用到JdbcUtils工具类(代码可参见:https://blog.csdn.net/qq_32224047/article/details/106722020)

情况一:不开启事务,在sql1和sql2中间加入异常int i = 1/0;

package cn.wy.jdbc.transaction;import cn.wy.jdbc.utils.JdbcUtils;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** JDBC 事务测试类* @author wyy* @date 2020/6/23 10:33*/
public class TransactionDemo1 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JdbcUtils.getConnection();String sql1 = "UPDATE myuser SET money= money-100 WHERE name=?";ps = conn.prepareStatement(sql1);ps.setString(1,"a");ps.executeUpdate();//加入错误,验证事务不开启,jdbc自动提交(a被扣钱,b没有加钱,因为默认提交,上面的sql1执行,slq2 为执行)int i = 1/0;String sql2 = "UPDATE myuser SET money= money+100 WHERE name=?";ps = conn.prepareStatement(sql2);ps.setString(1,"b");ps.executeUpdate();     }catch (Exception e){e.printStackTrace();}finally {JdbcUtils.close(rs,conn,ps);}}
}

代码执行前a和b用户金额都是1000

执行上面代码抛出异常

查询数据库a用户被扣除100元,但是b用户并没有增加100元,因为jdbc默认是开启了事务管理,当执行完成sql1后,出现异常,无法在执行到sql2,所有sql1执行后被提交,之后的数据就不在执行,就出现了下面的查询结果。

注意:在执行事务相关的操作时需要先检查一下当前数据库的隔离级别,MySQL默认隔离级别:repeatable read。可以用select @@tx_isolation;查询当前的隔离级别。当前的测试都是在默认的隔离级别下执行。如果是其他的隔离级别可能无法得到该测试想要的结果。

隔离级别的检查如下:

情况二:开启事务,在sql1和sql2中间加入异常int i = 1/0;

package cn.wy.jdbc.transaction;import cn.wy.jdbc.utils.JdbcUtils;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** JDBC 事务测试类* @author wyy* @date 2020/6/23 10:33*/
public class TransactionDemo1 {public static void main(String[] args) {//开启事务conn.setAutoCommit(false);conn = JdbcUtils.getConnection();String sql1 = "UPDATE myuser SET money= money-100 WHERE name=?";ps = conn.prepareStatement(sql1);ps.setString(1,"a");ps.executeUpdate();//加入错误,验证事务(a被扣钱,b没有加钱,因为默认提交,上面的sql1执行,slq2 为执行)int i = 1/0;String sql2 = "UPDATE myuser SET money= money+100 WHERE name=?";ps = conn.prepareStatement(sql2);ps.setString(1,"b");ps.executeUpdate();//提交事务conn.commit();}catch (Exception e){e.printStackTrace();//如果出现任何异常则,执行回滚if (conn!=null){try {conn.rollback();} catch (SQLException throwables) {throwables.printStackTrace();}}}finally {JdbcUtils.close(rs,conn,ps);}}
}

执行代码前先将a和b用户的账户金额都还原为1000

执行程序,抛出NullPointerException异常

查询数据库,结果并未改变,由于开启了事务,当出现异常时,后在catch中捕获后执行回滚(rollback),从而保证一个事务要么全部成功,要么全部不成功。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平