MySql事务及JDBC对事务的使用
一 、事务的几个重要特性
1. 原子性
事务内的每个内容不可分割,是一个统一的整体。或同时进行或同时消亡。
2.一致性
事务执行前和事务执行后,状态都是统一的。如A转B 100元,A和B数据总额度没有在这个转账过程中增加或者减小。
3.隔离性【isolation】
事务的隔离性指的是几个事务同时执行,事务a不应该干扰到事务b内的操作(在并发过程中很有可能会发生事务间的影响,例如脏读、不可重复读等。需要在编程的时候选择适当的方式进行选择)
3.可持久性
事务执行后的结果可以存储(序列化)到硬盘上,形成一个固定的内容存储起来。
二、事务的使用
事务的生命周期有2个,1开启事务 2提交或者回滚事务。事务开始后一定要提交或者回滚,以免引起数据库内存泄漏
使用Mysql开启事务及使用。
表内数据如下,现需要从a账号转100money到b账号。
具体步骤如下:
Mysql 中与事务使用有关的三个关键语句:
(1) 开启事务 : start transaction
(2)回滚事务 :rollback , 提交事务:commit
三、使用JDBC对事务进行调用
△注意: 事务只能对DML语句进行操作,对数据定义类语句DDL无法操作,例如建表、建立索引、建立分区等。
JDBC使用事务时,需要使用Java语言中的Connection对事务进行操作,具体的事务对应一个数据库连接。
1 package com.scl.test.transcaction; 2 3 import java.sql.Connection; 4 import java.sql.Statement; 5 6 import org.junit.Test; 7 8 public class TestTranscaction 9 { 10 11 @Test 12 public void testTransaction() throws Exception 13 { 14 Connection conn = null; 15 Statement statement = null; 16 17 try 18 { 19 conn = JDBCHelper.getConnection(); 20 21 conn.setAutoCommit(false); //开启事务,禁止自动提交 22 String sql1 = "update t_account t set t.money=t.money-100 where t.name ='a'"; 23 String sql2 = "update t_account t set t.money=t.money+100 where t.name ='b'"; 24 statement = conn.createStatement(); 25 // statement.setString(1, "a"); 26 27 statement.addBatch(sql1); 28 statement.addBatch(sql2); 29 30 31 statement.executeBatch(); 32 conn.commit(); //执行成功,提交事务 33 34 } 35 catch (Exception e) 36 { 37 conn.rollback(); //发生异常,事务回滚 38 } 39 finally 40 { 41 JDBCHelper.disposeConnect(statement, conn); 42 } 43 } 44 }
最后进行一个知识点补漏:JDBC执行多条sql脚本。
JDBC提供了执行多条sql语句的方法,使用PrepareStatement或Statement实例调用addBatch方法。目前发现该方法有几个缺点:
1. 无法通过PrepareStatement实例对多条不同的sql脚本进行参数化设置。因为PrepareStatement是由一个链接产生的,不能同时用一个prepareStatement实例同时对应两个不同的预处理文件。
如需求:需要插入更新一张表里面的两行不同的数据。
sql1: update t_account t set t.money=t.money-100 where t.name ='a';
sql2: update t_account t set t.money=t.money-100 where t.name ='b';
这时候没办法使用prepareStatement的addBatch方法执行操作。见代码:
1 @Test 2 public void testTransaction() throws Exception 3 { 4 Connection conn = null; 5 Statement st = null; 6 PreparedStatement pst = null; 7 try 8 { 9 conn = JDBCHelper.getConnection(); 10 11 conn.setAutoCommit(false); 12 String sql1 = "update t_account t set t.money=t.money-100 where t.name =?"; 13 String sql2 = "update t_account t set t.money=t.money+100 where t.name =?"; 14 pst = conn.prepareStatement(sql1); 15 // 只能批量执行某一条固定的sql语句,并且进行参数化设置 16 pst.setString(1, "a"); // 为name 为 a的用户减少100元 17 pst.addBatch(); 18 pst.setString(1, "b"); // 为name 为b的用户减少100元 19 pst.addBatch(); 20 pst.executeBatch(); // 只能在执行完成以后提交一次,然后改成新的sql脚本。但容易引发内存泄漏 21 22 pst = conn.prepareStatement(sql2); // 内存泄漏,存在未关闭的链接 23 pst.setString(1, "c"); 24 pst.addBatch(); 25 pst.executeBatch(); 26 conn.commit(); 27 28 } 29 catch (Exception e) 30 { 31 conn.rollback(); 32 } 33 finally 34 { 35 JDBCHelper.disposeConnect(pst, conn); 36 } 37 }
2. 使用statement实例对多条脚本进行提交(只提交一次,但无法进行预编译sql脚本)
1 @Test 2 public void testTransaction() throws Exception 3 { 4 Connection conn = null; 5 Statement st = null; 6 PreparedStatement pst = null; 7 try 8 { 9 conn = JDBCHelper.getConnection(); 10 11 conn.setAutoCommit(false); 12 String sql1 = "update t_account t set t.money=t.money-100 where t.name =?"; 13 String sql2 = "update t_account t set t.money=t.money+100 where t.name =?"; 14 pst = conn.prepareStatement(sql1); 15 // 只能批量执行某一条固定的sql语句,并且进行参数化设置 16 pst.setString(1, "a"); // 为name 为 a的用户减少100元 17 pst.addBatch(); 18 pst.setString(1, "b"); // 为name 为b的用户减少100元 19 pst.addBatch(); 20 pst.executeBatch(); // 只能在执行完成以后提交一次,然后改成新的sql脚本。但容易引发内存泄漏 21 22 pst = conn.prepareStatement(sql2); // 内存泄漏,存在未关闭的链接 23 pst.setString(1, "c"); 24 pst.addBatch(); 25 pst.executeBatch(); 26 conn.commit(); 27 28 } 29 catch (Exception e) 30 { 31 conn.rollback(); 32 } 33 finally 34 { 35 JDBCHelper.disposeConnect(pst, conn); 36 } 37 }