深入详解数据库事务(开发必用)

 一.事务的概念:

一组逻辑操作单元,时数据从一个状态转换到另一个状态。

 二.事务处理的原则:

        保证所有的事务都被当做一个操作单元来执行,即使出现了故障,也不能改变这种处置原则。要么与事务相关的数据全部被修改,并永远的提交保存下来,要么所有的事务全部回滚到事务没被执行的状态。

三.那些操作会影响数据库的提交

(一).数据库连接一旦断开,数据库的数据都被会提交

(二).DDL操作完成,数据自动提交,并且无法回滚

(三).DML操作默认为提交,但是可以通过 set auto commit = false;来设置不允许自动提交

四.模拟事务提交

事务执行过程图:

 

 

通用的包含事务的更新方法

    /**
     * 包含事务的通行的方法实现增删改
     * @param conn 从外部传入的数据库连接,意图在执行完一次DML操作时,数据库的连接不会断开,
     *             确保事务不会因为连接断而出现提交的现象
     * @param sql 增删改语句
     * @param args 占位符?个数不一定,根据sql占位符的个数,传入对应数量object
     */
    public static int update(Connection conn,String sql,Object... args){
        int temp = -1;
        PreparedStatement pst = null;
        try{
            //2.预编译sql语句
            pst = conn.prepareStatement(sql);
            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                pst.setObject(i+1,args[i]);
            }
            //4.执行
            /*
            pst.execute();如果是查询操作返回的是true
                            如果是更新操作返回的是false;
            pst.executeUpdate();返回执行操作以后受影响的行数
             */
//            pst.execute();
            temp = pst.executeUpdate();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //5.释放资源
            closeResource(null,pst);
        }
        return temp;
    }

(一).模拟存在异常情况下的102给103转账100元操作;

public void testTransaction() throws Exception {
        Connection conn = null;
        //102向103转账100元
        try{
            conn = JDBCUtils.getConnection();
            //关闭DML自动调教功能
            conn.setAutoCommit(false);
            String sql1 = "update user_tab set balance = balance - 100 where id = ?";
            update(conn,sql1,102);
            //模拟异常
            System.out.println(10/0);
            //103接受102转账100元
            String sql2 = "update user_tab set balance = balance + 100 where id = ?";
            update(conn,sql2,103);
            //提交事务
            conn.commit();
            System.out.println("转账成功");
        }catch (Exception e ){
            e.printStackTrace();
            //出现异常数据回滚
            conn.rollback();
        }finally {
//将自动提交功能设置还原为true
            conn.setAutoCommit(true);
            JDBCUtils.closeResource(conn,null);
        }
    }

转账失败

 (二).模拟不存在异常下转账的情况

public void testTransaction() throws Exception {
        Connection conn = null;
        //102向103转账100元
        try{
            conn = JDBCUtils.getConnection();
            //关闭DML自动调教功能
            conn.setAutoCommit(false);
            String sql1 = "update user_tab set balance = balance - 100 where id = ?";
            update(conn,sql1,102);
            //模拟异常
//        System.out.println(10/0);
            //103接受102转账100元
            String sql2 = "update user_tab set balance = balance + 100 where id = ?";
            update(conn,sql2,103);
            //提交事务
            conn.commit();
            System.out.println("转账成功");
        }catch (Exception e ){
            e.printStackTrace();
            //出现异常数据回滚
            conn.rollback();
        }finally {
//将自动提交功能设置还原为true
            conn.setAutoCommit(true);
            JDBCUtils.closeResource(conn,null);
        }
    }

转账成功

四.事务的ACID属性

(一).事务的属性

1.原子性:

        原子性是指事务时一个不可分割的工作单元,事务中的操作要么都发生,要么都不发生

2.一致性:

        事务必须使数据库从一个一致状态转换到另一个一致状态

3.隔离性:

        事务并发在执行时,各个事务之间的数据互不干扰的,各个事务 之间的数据不受任何影响

4.持久性:

        事务一旦被提交,就是永久性的改变,接下来发生的事或数据库故障都不会对其有任何我影响。

(二).事务的并发问题

1.脏读:

        读出了未提交的数据。

        事务T1,事务T2,T1读入一个数据段A,读出值为1,此时T2修改了A的值,T1将A= 2的值读出,但此时T2撤销了事务,回滚到A=1的阶段。此时T1读出的数据无效,即为脏读:

2.不可重复读:

只读提交的数据,并且第一次读出来是什么,在提交之前,读出来的东西和第一次保持一致;

        事务T1在第一次读到A=1,在读到以后,无论其他事务(T2)对字段A怎么更新提交(A=2),T1在提交前读出A字段永远都是A=1;

3.幻读:

事务T1第一次读出100条数据,T2插入3条数据并提交,T1提交后,T1再次读,读出103条数去,即为幻读,

(三).隔离级别

1. READ UNCOMMITED(读未提交的数据)

2. READ COMMITED(读已提交的数据)

3. REPEATED READ(可重复读)

4. 串行化:SERIALIZABLE(各个事务顺序执行)

(四).java代码实现隔离级别(框架必用,提前打基础

package Transaction;

import UpAndSelMethod.Method;
import jdbcUtil.JDBCUtils;
import org.junit.Test;
import userBean.User;

import java.sql.Connection;
//*********卧槽,为啥我设置隔离级别为读未提交却读不出来修改了却为体骄傲的数据,麻了******************
public class Test隔离级别 {
    //**************************测试事务并发问题与隔离级别的关系*********************************

    //测试查询
    @Test
    public void testTransactionSelect() throws Exception {
        Connection conn = JDBCUtils.getConnection();
        //设置sql语句
        String sql = "select id,name,balance from user_tab where id = ?";
        //查看当前连接的的隔离级别
        System.out.println(conn.getTransactionIsolation());
        //设置当前连接的隔离级别为读未提交
        conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
        System.out.println(conn.getTransactionIsolation());
        User user = Method.selectinfo(User.class, sql, 103);
        System.out.println(user);
    }
    //测试更新
    @Test
    public void testTransactionUpdate() throws Exception {
        //获取连接
        Connection conn = JDBCUtils.getConnection();
        //设置自动提交为false
        conn.setAutoCommit(false);
        //sql
        String sql = "update user_tab set balance = ? where id = ?";
        //执行更新操作
        Method.update(conn,sql,5000,103);
        //线程睡眠15秒
        Thread.sleep(15000);
        //睡眠结束
        System.out.println("修改完成");

    }

}

 


版权声明:本文为qq_52655865原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。