JDBC入门九:JDBC实现事务(十分重要!!!)

事务。MySQL事务是对于更新(插入,修改,删除)来说的,查询好像没有事务不事务一说。

目录

一:JDBC事务介绍 

1.事务及事务区:

(1)【commit提交】

(2)【rollback回滚】

2.JDBC两种事务模式:​

(1)自动提交事务模式:

(2)手动提交事务模式:

二:事务案例

1.没有使用【手动提交事务】模式时

(1)TransactionSample类编写

(2)运行结果 

(3)原因分析

2.使用【手动提交事务】模式时

(1)TransactionSample类编写(出错的时候)

(2)运行结果:(出错的时候)

(3)程序没有出错,正常执行


一:JDBC事务介绍 

MySQL之所以能在实际中使用,就是因为其有事务机制。MySQL,Oracle这样的关系型数据库都是支持事务的;也有很多小众的数据库是不支持事务的。

1.事务及事务区:

(1)【commit提交】

● 应用程序在写入数据时,并不是直接把数据放入到表中,而是先把数据放在事务区中;

● 事务区是MySQL自带的;如果数据很小的时候,事务区中的数据放在内存中,以加快处理速度;如果数据比较大的时候,其会用一块硬盘空间作为事务缓冲;

以A借给B100元为例:

       首先,程序对A的余额作了减法操作,这是一个写操作,但是其不会直接反映到数据表中,而是先把这个操作的中间结果放在事务区中;

       然后,程序对B的余额作了加法的操作,同样会把处理的中间结果放在事务区中

       最后,当程序对这两个更新操作全部执行完以后,会主动的向MySQL发起一个commit提交的命令,这个commit提交命令会直接作用到MySQL事务区上;当MySQL看到程序进行commit事务提交后,于是MySQL的事务区会将刚才的加和减两个操作一次性的写入到数据表中;

        即,进行程序的写操作的时候,两次的记录更新都是面向事务区的;只有当进行commit提交的时候,才由事务区真正的反映到数据表中(啰嗦一下:对于真正写入到数据表中的操作叫做“commit提交”);

        当提交成功后,事务区中的数据的数据就没有意义了,由MySQL自动的把这个事务区清空,等待下一次应用程序再向事务区中进行新数据的写入。

……………………………………………………

(2)【rollback回滚】

以A借给B100元为例:

        程序【对A的余额作了减法操作】操作正常;但是,由于某种原因程序【对B的余额作了加法的操作】的时候,程序报错了;那么JDBC的应用程序会向事务区发起一个【rollback回滚命令】;MySQL收到了这个回滚命令以后,其会直接将在事务区中原本已经处理好的【对A的余额作了减法操作】给清空;

        即程序一旦向MySQL提交了【rollback回滚命令】以后,无论之前做了多少数据的前置处理,MySQL都会直接将事务区清空,最终的数据表中不会产生任何实质的写入操作;

事务总结一句话:作为事务来说,要么一次性全部完成;要么将之前所有已经做的事情通过回滚全部撤销。

实际的Java代码,主要是通过控制【commit提交】和【rollback回滚】命令的时机,只有应用程序发起了这样的命令,MySQL才可以执行对应的操作。

……………………………………………………

2.JDBC两种事务模式:

(1)自动提交事务模式:

自动提交事务模式是JDBC的默认模式。在每一次执行写操作(新增、修改、删除)的时候,每完成一次SQL语句都会自动的提交事务;

如果在程序中没有显式的写【conn.setAutoCommit(true)】,其默认就是自动提交事务模式;

因为每一次执行写操作(新增、修改、删除)的以后,其都会立即提交;;所以,复杂的业务(需要多条写操作一起)的情况下,无法保证多数据的一致性;

……………………………………………………

(2)手动提交事务模式:

commit()方法和rollback()方法都是Connection接口中定义的;

话句话说,事务设置为手动提交,事务的的提交和事务回滚,都是需要通过Connection连接来调用方法实现的。很显然,因为事务本来就是对于一次数据库连接来说的嘛!!!!!


二:事务案例

比如,一个公司入职1000个员工,这1000个人要么一次性全部入职,要么一个都不入职。像这种要么全做,要么什么都不做的事情,基于数据库的事务进行处理,是非常合适的。

1.没有使用【手动提交事务】模式时

(1)TransactionSample类编写

TransactionSample类:

(1)没有使用事务;(2)预期要求是,1000个人要么全部入职,要么一个也不入职;(3)为了模拟过程中出现的程序报错终端,使用了一个抛出异常(没有对这个抛出的异常捕获啦,目的就是使程序中断执行);

package com.imooc.jdbc.sample;

import com.imooc.jdbc.common.DbUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TransactionSample {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = DbUtils.getConnection();
            String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";
            for (int i = 4000; i < 5000; i++) {
                if (i == 4005) {
                    throw new RuntimeException("插入失败");
                }
                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, i);
                pstmt.setString(2, "员工" + i);
                pstmt.setFloat(3, 4000);
                pstmt.setString(4, "市场部");
                pstmt.executeUpdate();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            DbUtils.closeConnection(null,pstmt,conn);
        }

        System.out.println("你好啊。");
    }
}

(2)运行结果 

运行结果:运行结果不符合预期(1000个人要么全部入职,要么一个都不要入职);

额外说明一点:因为上面程序中是抛出的【RuntimeException】,而在catch块中没有对【RuntimeException】的捕获,所以异常发生的时候,程序会中断,因此最后的【System.out.println("你好啊。");】没有执行。

(3)原因分析

失败原因分析:

        ● 默认情况下,JDBC会使用自动提交的模式;

        ● 自动提交:在前面执行的过程中,每执行一次update,即每执行一次【pstmt.executeUpdate();】,就立马对数据进行提交。所以,上面会出现【程序中断,但数据表中却出现了5条数据】的情况。

为了解决这个问题,需要在得到数据库连接对象之后,对其进行设置,以采用【手动提交事务】模式;


2.使用【手动提交事务】模式时

(1)TransactionSample类编写(出错的时候)

事务的设置,都是针对Connection对象来的!!!

package com.imooc.jdbc.sample;

import com.imooc.jdbc.common.DbUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TransactionSample {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = DbUtils.getConnection();
            conn.setAutoCommit(false);  // 关闭自动提交;
            String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";
            for (int i = 4000; i < 5000; i++) {
                if (i == 4005) {
                    throw new RuntimeException("插入失败");
                }
                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, i);
                pstmt.setString(2, "员工" + i);
                pstmt.setFloat(3, 4000);
                pstmt.setString(4, "市场部");
                pstmt.executeUpdate();
            }
            conn.commit();//提交数据
        }catch (Exception e) { //一旦上面在执行的过程中,抛出里异常;在catch块中,必须要对其进行捕捉;
            e.printStackTrace();
            try {
                if (conn != null && conn.isClosed() == false) {
                    conn.rollback();  //数据回滚
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            DbUtils.closeConnection(null, pstmt, conn);
        }

        System.out.println("你好啊。");
    }
}

(1)当【conn.setAutoCommit(false); 】关闭自动提交后,每一次执行【pstmt.executeUpdate();】的时候,所产生的中间数据会被放入到事务区中;

(2)在程序报错,捕捉异常,然后进行回滚的时候,catch块只保留了最大的【catch (Exception e)】;;;;;然后把回滚的操作放在了catch块中,这或许也是为什么只留最大的Exception的原因吧,。

(3)异常被捕获后,程序不会中中断,会继续执行;

(2)运行结果:(出错的时候)

(3)程序没有出错,正常执行

当程序正常执行的时候:

把这个异常抛出给注释掉,这个过程会正常执行;

运行效果:

在实际开发中,大多数的应用都是需要手动事务控制的。尤其是在金融项目中,数据强一致性的业务,必须要基于数据库的事务,而且要手动控制事务。


自我感觉,在实际中,某个业务所需的全部SQL操作,需要写在一个数据库连接(Connection对象)中,技术上是可以操作的,只是见得情况不多,没有感性的认识。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值