1. 概述
Spring事务是对原生事务的封装,我们还是需要了解如果直接使用JDBC的话,如何实现事务。
我们将向blog表插入两条数据(两次更新操作)定义为一个原子性操作,所以我们是期望这两个操作能同时成功、或者同时失败的。
2. 不使用事务
如果不使用事务,有可能会发生一个操作成功、另一个操作失败的情况,所以我们预期的原子性操作不成立。代码如下,可以看出因为执行过程中出现异常,导致最终只插入了一条数据,数据的完整性保护失败。
package org.maoge.jdbctran; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; /** * 原生JDBC事务使用实例 */ public class JdbcTransactionDemo { public static void main(String[] args) { actionWithoutTransaction(); } /** * 未使用事务 */ public static void actionWithoutTransaction() { String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "Easy@0122"; Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(url, username, password); String sql = "insert into blog(author,content,title)values(1,2,3)"; Statement stmt = conn.createStatement(); stmt.execute(sql);// 执行成功 int temp = 1 / 0;// 模拟抛出异常 stmt.execute(sql);// 未执行 } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } } }
3. 使用事务
如果使用事务的话,示例如下:
package org.maoge.jdbctran; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * 原生JDBC事务使用实例 */ public class JdbcTransactionDemo { public static void main(String[] args) { actionWithTransaction(); } /** * 使用事务 */ public static void actionWithTransaction() { String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "Easy@0122"; Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(url, username, password); conn.setAutoCommit(false);// 定义事务的起点 String sql = "insert into blog(author,content,title)values(1,2,3)"; Statement stmt = conn.createStatement(); stmt.execute(sql);// 没问题 int temp = 1 / 0;// 模拟抛出异常,导致回滚,所以上面的执行也回滚了 stmt.execute(sql);// 未执行 conn.commit();// 如果全部执行成功,则提交事务 } catch (Exception e) { try { conn.rollback();// 如果出现异常则回滚 } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } } }
分析上面的代码,就是我们在开始一个原子性操作之前(可能包含若干对数据库的操作),先开启事务,然后等几个操作都执行后再提交事务,此时对数据库的修改才生效;如果这中间发生异常,则执行回滚,则这些操作一个都不会生效。
从上面的示例中可以发现,几乎很多地方都是跟业务逻辑无关的代码,都是模板性质的代码,遵循如下结构:
try{
//开始事务
//执行具体操作
//提交事务
}catch(Exception e){
//回滚事务
//处理异常
}finally{
//释放资源
}
1
2
3
4
5
6
7
8
9
10
其中真正有意义的,只有具体操作部分,其他都是雷同的,所以可以封装起来。
4. 默认情况
那么如果不显示的指定conn.setAutoCommit(false);,是什么情况呢。我们尝试下,可见默认情况下事务是自动提交的,也就是执行一个sql就自动提交事务了,所以默认情况下执行sql后更新马上就持久化到数据库了。
package org.maoge.jdbctran; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * 原生JDBC事务使用实例 */ public class JdbcTransactionDemo { public static void main(String[] args) { testAutoCommit(); } /** * 使用事务 */ public static void testAutoCommit() { String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "Easy@0122"; Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(url, username, password); System.out.println(conn.getAutoCommit());//输出true } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } } }