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

2022年05月15日 阅读数:3
这篇文章主要向大家介绍深刻详解数据库事务(开发必用),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

 一.事务的概念:

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

 二.事务处理的原则:

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

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

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

(二).DDL操做完成,数据自动提交,而且没法回滚并发

(三).DML操做默认为提交,可是能够经过 set auto commit = false;来设置不容许自动提交框架

四.模拟事务提交测试

事务执行过程图:spa

 

 

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

    /**
     * 包含事务的通行的方法实现增删改
     * @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);
        }
    }

转帐失败code

 (二).模拟不存在异常下转帐的状况

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);
        }
    }

转帐成功blog

四.事务的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("修改完成");

    }

}