事务: 逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。
事务的特性:
原子性:事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性:必须是数据库从一个一致性状态变换到另一个一致性状态。
隔离性:多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其他事务的操作干扰,多个并发事务之间要相互隔离。
持久性:一个事务一旦被提交,他对数据库中的改变就是永久性的,接下来即时数据库发生故障也不会对齐又任何影响。
start transaction 开启事务
Rollback 回滚事务
Commit 提交事务
默认情况下,Connection会自动提交sql语句:
Connection.setAutoCommit(false);
start transaction;
Connection.rollback();
rollback;
Connection.commit();
commit;
案例:
package bank; import static org.junit.Assert.*; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import org.junit.Test; import utils.JdbcUtils; public class TransactionTest { @Test public void test() throws Exception { Connection conn = JdbcUtils.getConnection(); PreparedStatement pstmt1 = conn.prepareStatement("update bank set money = money - ? where id = ?"); pstmt1.setFloat(1, 9800); pstmt1.setInt(2, 1); int n = pstmt1.executeUpdate(); System.out.println("pstmt1="+n); //使得执行不成功 System.out.println(10/0); PreparedStatement pstmt2 = conn.prepareStatement("update bank set money = money + ? where id = ?"); pstmt2.setFloat(2, 9800); pstmt2.setInt(2, 2); n = pstmt2.executeUpdate(); System.out.println("pstmt2="+n); JdbcUtils.release(null, pstmt1, conn); JdbcUtils.release(null, pstmt2, conn); } //采用事务来处理转账失败情况 @Test public void testTransaction() throws Exception { Connection conn = JdbcUtils.getConnection(); PreparedStatement pstmt1 = null; PreparedStatement pstmt2 = null; //事务需要在一个连接对象中执行 //不允许自动提交 conn.setAutoCommit(false); //抓异常 try{ pstmt1 = conn.prepareStatement("update bank set money = money - ? where id = ?"); pstmt1.setFloat(1, 9800); pstmt1.setInt(2, 1); int n = pstmt1.executeUpdate(); System.out.println("pstmt1="+n); //使得执行不成功 // System.out.println(10/0); pstmt2 = conn.prepareStatement("update bank set money = money + ? where id = ?"); pstmt2.setFloat(1, 9800); pstmt2.setInt(2, 2); n = pstmt2.executeUpdate(); System.out.println("pstmt2="+n); //手动提交 conn.commit(); }catch(Exception e){ //让事务进行回滚 conn.rollback(); System.out.println("error"); } JdbcUtils.release(null, pstmt1, conn); JdbcUtils.release(null, pstmt2, conn); } }
事务的隔离性
脏读:一个事务读取了另外一个事务未提交的数据。
假设A向B转账100元,对应sql为:
update account set money = money - 100 where name = 'a';
update account set money = money + 100 where name = 'b';
当第一条语句执行完之后,第二条还没有执行,如果此时B查询自己的账户,就会发现自己多了100元。如果A等B走后再回滚,B就会损失100元。
不可重复读(针对一条记录的,同一条记录前后不一样)
在一个事务内读取表中的某一行数据,多次读取结果不同。
第二次查询时,转入100元,两次查询不一致。很多人认为这种情况是正常的,当然是以后边查询结果为准。可以考虑这样的情况,比如银行的程序要讲查询结果输出到电脑屏幕和写入文件中,结果在一个事务中,针对输出结果的目的地,进行两次查询不一致,导致屏幕和文件的结果不一致,工作人员也不知道以哪个为准了。
和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
虚读(幻读):同一张表前后不一样的记录数。
数据库定义了四种隔离级别:
Serializable: 可避免脏读,不可重复读,虚读情况的发生。
Repeatable read: 可避免脏读,不可重复读情况的发生。
Read committed:可避免脏读情况发生。
Read uncommitted: 最低级别,以上情况均无法保证。
查询当前事务隔离级别: select @@tx_isolation
设置事务隔离级别: set transaction isolation Repeatable read;
数据库的锁:
共享锁:一个事务开启时,使用了共享锁,其他事务也可以设置共享锁,读数据没有问题。
select * from student in share mode;
排它锁:一个事务开启时,使用了排它锁,其他事务只能等待。update insert 等语句会自动加排它锁。
select * from student for update;
常用操作:
start transaction; + commit ; 开启结束一个事务
set character_set_client=gbk;
set character_set_results=gbk;