数据库开发技术java方向_Java开发工程师(Web方向) - 03.数据库开发 - 第4章.事务

第4章--事务

事务原理与开发

事务Transaction:

什么是事务?

事务是并发控制的基本单位,指作为单个逻辑工作单元执行的一系列操作,且逻辑工作单元需满足ACID特性。

i.e. 银行转账:开始交易;张三账户扣除100元;李四账户增加100元;结束交易。

事务的特性:ACID

原子性 Atomicity:整个交易必须作为一个整体来执行。(要么全部执行,要么全部不执行)

一致性 Consistency:整个交易总体资金不变

隔离性 Isolation:

case1: 若张三给李四转账过程中,赵五给张三转账了200元。两个交易并发执行。

T1   T2

读取张三余额100;

读取张三余额100;

给李四转账100,

更新张三余额为0;

交易结束                  赵五转入200,

更新张三余额为300

交易结束

case2: 脏读:张三给别人转账100之后张三存钱200,存钱后转账由于系统原因失败回滚。

读取一个事务未提交的更新

T1 T2

读取张三余额100

(转账) 更新张三余额0

读取张三余额0

T1 Rollback() (存钱) 更新张三余额200

T2结束(张三账户余额为200)

case3: 不可重复读:同一个事务,两次读取同一数值的结果不同,成为不可重复读。

T1张三读取自己余额为100;T2读取张三余额100;T2存钱更新为300;T1张三读取余额为300。T1中两次读取张三余额即为不可重复读。

case4: 幻读:两次读取的结果包含的行记录不一样。

T1读取所有用户(张三、李四);T2新增用户赵五;T1读取所有用户(3个);T1/T2结束。T1中两次读取的结果中行记录数不同,称为幻读。

需要避免上述cases的产生

隔离性:交易之间相互隔离,在一个交易完成之前,不能受到其他交易的影响

持久性 Durability:整个交易过程一旦结束,无论出现任何情况,交易都应该是永久生效的

使用JDBC进行事务控制:

Connection类中

.setAutoCommit():开启事务(若为false,则该Connection对象后续的sql都将作为事务来处理;若为true,则该Connection对象后续的所有sql都将作为单独的语句执行(默认为true))

.commit():事务被提交,即事务生效并结束

.rollback():回滚,回退到事务开始之前的状态

i.e.

ALTER TABLE user ADD Account int;UPDATE User SET Account = 100 WHERE id = 1;UPDATE User SET Account = 0 WHERE id > 1;

20180111002332081889.png

实现ZhangSi(1)给LiSan(2)转账的过程:

(非事务:)

public static voidTransferNonTransaction() {

Connection conn= null;

PreparedStatement ptmt= null;try{

conn=DriverManager.getConnection(DB_URL, USER_NAME, PASSWORD);

String sql= "UPDATE User SET Account = ? WHERE userName = ? AND id = ?;";//transfer 100 from ZhangSi(1) to LiSan(2)

ptmt =conn.prepareStatement(sql);

ptmt.setInt(1, 0);

ptmt.setString(2, "ZhangSi");

ptmt.setInt(3, 1);

ptmt.execute();

ptmt.setInt(1, 100);

ptmt.setString(2, "LiSan");

ptmt.setInt(3, 2);

ptmt.execute();

}catch(SQLException e) {

e.printStackTrace();

}finally{try{if (conn != null) conn.close();if (ptmt != null) ptmt.close();

}catch(SQLException e) {

e.printStackTrace();

}

}

}

执行完第一个ptmt.execute()后,数据库中ZhangSi的Account=0, LiSan的Account=0;

出现了一个中间状态,对于整个业务逻辑的实现是不可接受的。如果此时程序崩溃了将不可挽回。

(事务:)

public static voidTransferByTransaction() {

Connection conn= null;

PreparedStatement ptmt= null;try{

conn=DriverManager.getConnection(DB_URL, USER_NAME, PASSWORD);//Using Transaction mechanism

conn.setAutoCommit(false);

String sql= "UPDATE User SET Account = ? WHERE userName = ? AND id = ?;";

ptmt=conn.prepareStatement(sql);

ptmt.setInt(1, 0);

ptmt.setString(2, "ZhangSi");

ptmt.setInt(3, 1);

ptmt.execute();

ptmt.setInt(1, 100);

ptmt.setString(2, "LiSan");

ptmt.setInt(3, 2);

ptmt.execute();//Commit the transaction

conn.commit();

}catch(SQLException e) {//if something wrong happens, rolling back

if(conn != null) {try{

conn.rollback();

}catch(SQLException e1) {

e1.printStackTrace();

}

}

e.printStackTrace();

}finally{try{if (conn != null) conn.close();if (ptmt != null) ptmt.close();

}catch(SQLException e) {

e.printStackTrace();

}

}

}

若在第一个ptmt.execute()时断点,并查询数据库,结果为事务执行之前的状态,并不是中间状态。

直到conn.commit()方法执行完毕,事务中的所有操作在数据库中才有效。

Connection类中的检查点功能:

.setSavePoint():在执行过程中创建保存点,以便rollback()可以回滚到该保存点

.rollback(SavePoint savePoint):回滚到某个检查点

i.e.

public static voidrollbackTest() {

Connection conn= null;

PreparedStatement ptmt= null;//save point

Savepoint sp = null;try{

conn=DriverManager.getConnection(DB_URL, USER_NAME, PASSWORD);

conn.setAutoCommit(false);

String sql= "UPDATE User SET Account = ? WHERE userName = ? AND id = ?;";

ptmt=conn.prepareStatement(sql);

ptmt.setInt(1, 0);

ptmt.setString(2, "ZhangSi");

ptmt.setInt(3, 1);

ptmt.execute();//create a save point

sp =conn.setSavepoint();

ptmt.setInt(1, 100);

ptmt.setString(2, "LiSan");

ptmt.setInt(3, 2);

ptmt.execute();//throw an exception manually for the purpose of testing

throw newSQLException();

}catch(SQLException e) {//if something wrong happens, rolling back to the save point created before//and then transfer the money to Guoyi(3)

if(conn != null) {try{

conn.rollback(sp);

System.out.println("Transfer from ZhangSi(1) to LiSan(2) failed;\n"

+ "Transfer to GuoYi(3) instead");//other operations

ptmt.setInt(1, 100);

ptmt.setString(2, "GuoYi");

ptmt.setInt(3, 3);

ptmt.executeQuery();

conn.commit();

}catch(SQLException e1) {

e1.printStackTrace();

}

}

e.printStackTrace();

}finally{try{if (conn != null) conn.close();if (ptmt != null) ptmt.close();

}catch(SQLException e) {

e.printStackTrace();

}

}

}

事务的隔离级别:4个级别

读未提交(read uncommited):可能导致脏读

读提交(read commited):不可能脏读,但是会出现不可重复读

重复读(repeatable read):不会出现不可重复读,但是会出现幻读

串行化(serializable):最高隔离级别,不会出现幻读,但严格的并发控制、串行执行导致数据库性能差

N.B. 1. 事务隔离级别越高,数据库性能越差,但对于开发者而言编程难度越低。

2. MySQL默认事务隔离级别为重复读 repeatable read

JDBC设置隔离级别:

Connection对象中,

.getTransactionIsolation();

.setTransactionIsolation();

死锁分析与解决

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值