[JAVA]JDBC的手动事务开发技巧—实现批量增加员工

我们假设这样的场景,一家公司有一项新的人事管理系统,我们需要把公司1000名员工导入这个管理系统 。对此系统我们有一些基本要求,这1000名员工要么一次性全部导入系统,要么一个都不导入。对于这种情况就可以基于数据库的事务来进行处理。

那么我们通过代码形式来理解事务开发。

1.首先我们需要调用DbUtils类的getConnection方法获取数据库连接,DbUtils是封装数据库连接的工具类。

Connection conn = null;
PreparedStatement pstmt = null;
conn =DbUtils.getConnection();

2.定义一个SQL插入语句的字符串,指定向名为employee的表中插入数据,四个问号作为占位符,分别对应员工编号,员工姓名,工资和部门名称。

String sql ="insert into employee(eno,ename,salary,dname) values(?,?,?,?)";

3.生成循环,从1000开始到1999结束,生成1000条数据,if判断语句用于模拟插入错误的情况,以便后续学习。

for (int i=1000;i<2000;i++){
  if(i==1005){
    throw new RuntimeException("插入失败");
  //如果i的值为1005时,抛出RuntimeException运行时异常并给出提示信息
  .........
}
}

4.写入上述for循环中,每次循环使用数据库连接conn调用prepareStatement方法,并传入SQL语句字符串,创建一个PreparedStatement对象pstmt,用于执行具体的插入操作

pstmt= conn.prepareStatement(sql);

5.设置每一次循环后的变量 i 的值

 pstmt.setInt(1,i);
 pstmt.setString(2,"员工"+i);
 pstmt.setFloat(3,4000f);
 pstmt.setString(4,"市场部");

6.执行插入操作,将当前生成的员工数据插入到数据库中

pstmt.executeUpdate();

7. 释放连接

finally{
    DbUtils.closeConnection(null,pstmt,conn);
}

结果显示,由于上述if的判断导致的异常提示:

而数据表只显示了1000~1004之间的数据:

观察数据库的员工表,虽然成功插入前面循环语句中的五条数据,但是这里产生了严重的数据一致性问题,我们期望的是这1000名员工全部插入成功,如果中间出现了任何错误的话,把之前的数据都撤回。

对于这个需求,目前我们的数据库是无能为力的,因为我们在代码编写的过程中,我们并没有设置JDBC的事务模式,在默认的情况下,JDBC 会使用自动提交的方式。自动提交的含义是前面执行的过程中,每执行一次Update,立马对数据进行提交,所以即便是循环在了1005次时,抛出了异常,因为前面的五条数据已经完成了提交的工作,因此在我们数据表中出现了数据不完整的情况,为了解决这个问题,我们在得到数据库连接以后,需要再设置一个方法:

conn.setAutoCommit(false); //关闭自动提交

此时自动提交关闭以后,再次执行executeUpdate的时候,所产生的中间数据会被放到事务区中,不会直接提交给数据库,那么什么时候会被提交给数据库呢?

在我们的for循环完整执行以后,我们需要手动的调用conn.commit (),才可以对我们的事务区的数据进行提交,真正的写入到表中。

conn.commit(); //提交数据

但是,这里呢,可能会有一个意外情况,如果上述if循环中要抛出了异常的话,那就意味着之前成功提交过的数据都要在事务区中回滚撤销,即将之前已提交给事务区的数据全部删除,必须保证数据是一次性提交的,这个回滚撤销该怎么做呢?

  • 一旦try块中抛出了RuntimeException(if判断中的异常),则我们在catch中就必须要对它进行捕捉才行。
  • 判断当前连接是可用的情况下,才进行回滚操作
}catch (Exception e){ //Exception是所有异常的父类
   e.printStackTrace();
 try{                                   //rollback会抛出异常,用try—catch捕获
   if(conn !=null && !conn.isClosed()){ //当前连接是可用的状态下,执行回滚操作               
   conn.rollback();                     //回滚——将之前已经写入到事务区的数据取消掉
}catch(SQLException ex)               
   ex.printStackTrace();
}

 此时,再次执行程序,并查看MySQL数据表,发现采用了事务的方式,前五条数据并不会像之前一样被写入到事务表中。

我们再将if判断删去,看无异常的情况下,数据会不会全部插入。

可以看到,在没有if判断产生的异常情况下,我们想要的员工数据能全部插入。

上述操作完整代码实现:

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();   //调用DbUtils.getConnection()获取数据库连接
            //JDBC默认使用自动提交方式
            conn.setAutoCommit(false); //关闭自动提交
            String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";//定义插入数据的SQL语句
            for (int i = 1000; i < 2000; i++) {//编号生成1000到1999这1000条数据
                 if (i == 1005) {              //当符合条件时抛出异常
                    throw new RuntimeException("插入失败");
                }
                pstmt = conn.prepareStatement(sql);//将sql传入
                pstmt.setInt(1, i);
                pstmt.setString(2, "员工" + i);
                pstmt.setFloat(3, 4000f);
                pstmt.setString(4, "市场部");
                pstmt.executeUpdate();
            }
            conn.commit(); //提交数据
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (conn != null && !conn.isClosed())
                    conn.rollback();  //回滚数据
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeConnection(null, pstmt, conn);
        }
    }
}

通过这个案例,可以了解到事务是对我们数据一致性的保证,通过增加事务区这个缓冲的机制,去等待我们程序发起commit提交或者rollback回滚。一旦commit成功提交,所有在数据区做完的数据都会写入到表中,但是如果是rollback的话,就永远不会写入目标表,而是将事务区做好的数据撤销掉。

在日常开发中,大多数的应用都是要手动事务控制,尤其是做金融项目,比如说在A向B转账过程中,A的账户要减多少钱,B的账户相应的要加多少钱,像这种数据强移的操作,必须要基于数据库的手动事务模式,才会被认为是可靠的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值