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),从而保证一个事务要么全部成功,要么全部不成功。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章