JDBC事务自动提交机制
首先我们先来看一段代码:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21点48分 */ public class JDBCTest_03 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_03.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值复制给新变量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注册驱动 Class.forName(driver); try { //获取链接 conn = DriverManager.getConnection(url, user, password); String sql = "UPDATE LOGINNAME SET NAMEPASS = ? WHERE NAME = ? "; //获取数据库操作对象 ps = conn.prepareStatement(sql); //添加sql数据 ps.setString(1,"guisha"); ps.setString(2,"Mrcheng"); //执行SQL语句,返回条数 int i = ps.executeUpdate(); System.out.println(i); ps = conn.prepareStatement(sql); ps.setString(1,"wuxin"); ps.setString(2,"wuxin"); int i1 = ps.executeUpdate(); System.out.println(i1); } catch (SQLException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //关闭资源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
代码中,我们更新2条数据,但是在 int i = ps.executeUpdate(); 这里会自动提交事务,可以理解为提交SQL语句对数据库修改
我们需要了解JDBC的事务是自动提交的,什么是自动提交?
1)只执行任意一条DML语句,则提交一次。JDBC默认的提交方式是自动提交,是JDBC默认的事务行为
2)在实际开发业务中,通常都是N条DML语句共同联合才能完成的,必须保证DML语句在一个事务中同时成功或者同时失败
我们来看一个小例子来验证以下JDBC的事务是否是自动提交机制!
两个人转账的小案例:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21点48分 */ public class JDBCTest_04 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_04.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值复制给新变量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注册驱动 Class.forName(driver); try { //获取链接 conn = DriverManager.getConnection(url, user, password); String sql = "UPDATE center_cat SET user_baln = ? WHERE USER_NAME = ? "; //获取数据库操作对象 ps = conn.prepareStatement(sql); //添加sql数据 ps.setDouble(1,10000); ps.setString(2,"小明"); //执行SQL语句,返回条数 int i = ps.executeUpdate(); System.out.println(i); //自定义NullPointerException String s = null; s.toString(); ps.setString(1,"10000"); ps.setString(2,"王老头"); i += ps.executeUpdate(); System.out.println(i); } catch (SQLException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //关闭资源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
运行之后会出错,我们程序中定义的 NullPointerException
再查看数据库,数据库执行一半出现异常终止,小明资金共20000,现在能够看出来 小明明学转出的10000元消失不见了,这里就是每执行一条DML语句,事务会自动提交一次
现在我们设置是为手动开启和手动提交,如果中间出现错误,我们回滚数据
代码如下:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21点48分 */ public class JDBCTest_04 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_04.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值复制给新变量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注册驱动 Class.forName(driver); try { //获取链接 conn = DriverManager.getConnection(url, user, password); //开启事务 conn.setAutoCommit(false); String sql = "UPDATE center_cat SET user_baln = ? WHERE USER_NAME = ? "; //获取数据库操作对象 ps = conn.prepareStatement(sql); //第一次给?传值 ps.setDouble(1,10000); ps.setString(2,"小明"); //执行SQL语句,返回条数 int i = ps.executeUpdate(); System.out.println(i); // 自定义NullPointerException 用来终止正在执行的程序,用于演示JDBC事务自动提交机制的问题 // 想正常实执行代码,请删除自定义自定义NullPointerException异常 String s = null; s.toString(); //第二次给?传值 ps.setString(1,"10000"); ps.setString(2,"王老头"); i += ps.executeUpdate(); System.out.println(i == 2 ? "转账成功!": "转账失败!"); //提交事务 conn.commit(); } catch (SQLException e) { / //数据回滚 if (conn !=null){ try { conn.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } } e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //关闭资源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
以上代码就算出错,数据会进行回滚,重点使用了conn.setAutoCommit(false)(手动开启事务)、conn.commit()(手动提交事务)和conn.rollback(手动回滚数据)
正常执行如下:
package com.guisha.JDBC; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; /** * 方法描述 * @since: 1.0.0 * @param: * @return: * @author: Mr.cheng * @date: 2021年1月6日 21点48分 */ public class JDBCTest_04 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { InputStream input = JDBCTest_04.class.getClassLoader().getResourceAsStream("db.properties"); Properties property = new Properties(); try { //配置文件取值复制给新变量 property.load(input); String driver = property.getProperty("driver"); String url = property.getProperty("url"); String user = property.getProperty("user"); String password = property.getProperty("password"); //注册驱动 Class.forName(driver); try { //获取链接 conn = DriverManager.getConnection(url, user, password); //开启事务 conn.setAutoCommit(false); String sql = "UPDATE center_cat SET user_baln = ? WHERE USER_NAME = ? "; //获取数据库操作对象 ps = conn.prepareStatement(sql); //第一次给?传值 ps.setDouble(1,10000); ps.setString(2,"小明"); //执行SQL语句,返回条数 int i = ps.executeUpdate(); System.out.println(i); //第二次给?传值 ps.setString(1,"10000"); ps.setString(2,"王老头"); i += ps.executeUpdate(); System.out.println(i == 2 ? "转账成功!": "转账失败!"); //提交事务 conn.commit(); } catch (SQLException e) { if (conn !=null){ try { conn.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } } e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }finally{ //关闭资源 try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }