JDBC_07悲觀鎖和樂觀鎖
阿新 • • 發佈:2021-07-29
JDBC_07悲觀鎖和樂觀鎖
1.悲觀鎖和樂觀鎖的概念
1.1悲觀鎖
悲觀鎖就是比較悲觀。我在拿一條記錄時,生怕別人修改這條記錄,於是就把它鎖住,不讓別人用。直到我使用完才解鎖。
悲觀鎖的語法格式是:
select ... from ... where ... for update;
如:
select empno,ename,job,sal from emp where empno = 7499 for update;
在mysql中,當使用for update語句時,決定行鎖還是表鎖的是條件欄位是否能使用索引(如主鍵、unique等)。
如果能則為行鎖,不能則為表鎖。
所以在使用悲觀鎖時,條件欄位最好為主鍵或有unique約束的欄位,鎖別的可能會將整張表都鎖住,導致程式執行效率降低。
1.2樂觀鎖
樂觀鎖就是比較樂觀,當我要修改一條記錄時,不怕被別人修改,所有人都可以拿來用。
當我修改完後,在提交前,會再看一眼這條記錄的版本號:
- 如果和被我修改之前的版本號一樣,我就將修改的資料提交,然後更改版本號。
- 如果和被我修改之前的版本號不一樣,我就將我的操作回滾。
2.演示悲觀鎖機制
2.1用到的表
emp:
+--------+-----------+---------+ | ename | job | sal | +--------+-----------+---------+ | SMITH | CLERK | 1800.00 | | ALLEN | SALESMAN | 1600.00 | | WARD | SALESMAN | 1250.00 | | JONES | MANAGER | 4975.00 | | MARTIN | SALESMAN | 1250.00 | | BLAKE | MANAGER | 4850.00 | | CLARK | MANAGER | 4450.00 | | SCOTT | ANALYST | 3000.00 | | KING | PRESIDENT | 5000.00 | | TURNER | SALESMAN | 1500.00 | | ADAMS | CLERK | 2100.00 | | JAMES | CLERK | 1950.00 | | FORD | ANALYST | 3000.00 | | MILLER | CLERK | 2300.00 | +--------+-----------+---------+
2.2演示流程
編寫程式A和程式B,分別代表事務A和事務B。
事務A執行:
select ename,job,sal from emp where job = 'MANAGER' for update;
事務B執行:
update emp set sal = sal + 1000 where job = 'MANAGER';
在程式A中,在提交事務處打一個斷點,然後debug,讓事務A停住,
此時,執行程式B,檢視程式B的反應。
2.3程式原始碼
2.3.1程式A
package com.tsccg.jdbc.transaction; import com.tsccg.jdbc.util.JdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @Author: TSCCG * @Date: 2021/07/29 14:31 * 演示悲觀鎖 * 事務A */ public class TransactionDemo03 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JdbcUtil.connect(); //開啟事務 conn.setAutoCommit(false); //使用悲觀鎖鎖住job為MANAGER的所有記錄 String sql = "select ename,job,sal from emp where job = ? for update"; ps = conn.prepareStatement(sql); ps.setString(1,"MANAGER"); rs = ps.executeQuery(); while (rs.next()) { System.out.println(rs.getString("ename") + "," + rs.getString("job") + "," + rs.getDouble("sal")); } //提交事務(事務結束) conn.commit(); } catch (SQLException throwables) { if (conn != null) { try { //回滾事務(事務結束) conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } throwables.printStackTrace(); } finally { JdbcUtil.close(conn,ps,rs); } } }
2.3.2程式B
package com.tsccg.jdbc.transaction;
import com.tsccg.jdbc.util.JdbcUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @Author: TSCCG
* @Date: 2021/07/29 14:41
* 演示悲觀鎖
* 事務B
*/
public class TransactionDemo04 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.connect();
conn.setAutoCommit(false);
//使用悲觀鎖鎖住job為MANAGER的所有記錄
String sql = "update emp set sal = sal + 1000 where job = ?";
ps = conn.prepareStatement(sql);
ps.setString(1,"MANAGER");
int count = ps.executeUpdate();
System.out.println(count > 0 ? "修改成功" : "修改失敗");
conn.commit();
} catch (SQLException throwables) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
throwables.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,null);
}
}
}