java学习笔记39(sql事物)
在之前的学习中,我们学习了使用PreparedStatement类,使用这个类消除了sql注入的隐患,可是,还有些一些其他的隐患,这里以银行转账业务为例,
假设 一个银行,张三在里面存了1000元,李四也在里面存了1000元,张三给李四转账500,那么张三的钱少500,李四的钱多500,可是如果转账的瞬间,张三的钱扣过后,给李四增加钱之前,假设停电了,那么钱就不见了,具体如下:
创建一个假设银行的数据表:
create table account ( id int auto_increment primary key, NAME varchar(10) null, balnce double null );
当张三给李四转账500元时,我们用两句语句分开执行,java实现具体代码如下:
package com.zs.Demo; import JDBCUtils.JDBCUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Scanner; public class ZhuanZhang { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入转账金额:"); double money = sc.nextDouble(); zhuanzhang(money); } private static void zhuanzhang(double money) { Connection conn = JDBCUtils.getConnection(); PreparedStatement pre=null; try { //张三减少money元 String sql = "update account set balnce = balnce-? where name=?;"; pre = conn.prepareStatement(sql); pre.setDouble(1,money); pre.setString(2,"张三"); pre.executeUpdate(); //李四增加money元 String sql2="update account set balnce = balnce+? where name=?;"; pre = conn.prepareStatement(sql2); pre.setDouble(1, money); pre.setString(2,"李四"); pre.executeUpdate(); System.out.println("转账成功"); } catch (SQLException e) { System.out.println("转账失败"); e.printStackTrace(); }finally{ JDBCUtils.close(conn,pre); } } }
运行结果:
我们现在在张三和李四转账之间插入一个异常,假设停电,具体代码在上面代码中添加一句异常,如下:
package com.zs.Demo; import JDBCUtils.JDBCUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Scanner; public class ZhuanZhang { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入转账金额:"); double money = sc.nextDouble(); zhuanzhang(money); } private static void zhuanzhang(double money) { Connection conn = JDBCUtils.getConnection(); PreparedStatement pre=null; try { //张三减少money元 String sql = "update account set balnce = balnce-? where name=?;"; pre = conn.prepareStatement(sql); pre.setDouble(1,money); pre.setString(2,"张三"); pre.executeUpdate(); /*这里插入异常,0不能作为除数,所以这里会报错,用这种方式来假设停电之类的异常,我们会发现上 面的语句已经执行了,到这里出现异常下面的语句不能执行,张三的钱扣了,李四的钱却没有增加,钱不见了*/ System.out.println(100/0); //李四增加money元 String sql2="update account set balnce = balnce+? where name=?;"; pre = conn.prepareStatement(sql2); pre.setDouble(1, money); pre.setString(2,"李四"); pre.executeUpdate(); System.out.println("转账成功"); } catch (SQLException e) { System.out.println("转账失败"); e.printStackTrace(); }finally{ JDBCUtils.close(conn,pre); } } }
运行结果:
这时我们发现钱少了500,不见了,为了解决这种问题,数据库中,我们把这两种操作捆绑成一个事物,执行结果有两种,成功或者失败,不可以分开执行,
执行成功称为:提交事务,出现异常或者失败称:回滚事务。
在该例种,我们使用事物来处理时,思路如下:
1.获取链接
2.开启事物
3.获取PreparedStatement对象
4.执行两次更新操作,
5.正常情况提交事务,出现异常时回滚事物
6.关闭资源
代码:
package com.zs.Demo; import JDBCUtils.JDBCUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Scanner; /*需求分析:模拟银行转账业务,张三给李四转账,当张三的钱扣除后,李四的钱增加*/ public class ShiWu { private static PreparedStatement pre; public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("请输入转账金额:"); double money = sc.nextDouble(); zhuangzhang(money); } private static void zhuangzhang(double money) { //获得连接 Connection conn=JDBCUtils.getConnection(); try { // 开启事物功能 conn.setAutoCommit(false); // 执行sql语句 String sql="update account set balnce=balnce-? where name=?"; String sql2="update account set balnce = balnce+? where name=?"; pre= conn.prepareStatement(sql); pre.setDouble(1,money); pre.setString(2,"张三"); /*这里添加异常,到这里时出现异常,所以事物不成功,回滚事物,去掉下面异常语句,程序正常提交事务*/ System.out.println(100/0); pre.executeUpdate(); pre= conn.prepareStatement(sql2); pre.setDouble(1,money); pre.setString(2,"李四"); pre.executeUpdate(); // 提交事务 conn.commit(); System.out.println("转账成功"); } catch (Exception e) { try { // 回滚事务 conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); System.out.println("转账失败"); } // 关闭资源 JDBCUtils.close(conn,pre); } }
运行结果:
正常提交的话,将代码中的异常语句注销就可以了,出现异常时,就会回滚事务,我们发现钱没有少,整个事务执行失败,异常前面的代码也没有成功执行。