I have written a sample code:-
First run this on Oracle DB:-
Create table AccountBalance
(
id integer Primary Key,
acctName varchar2(255) not null,
acctBalance integer not null,
bankName varchar2(255) not null
);
insert into AccountBalance values (1,'Test',50000,'Bank-a');
Now run the below code
package com.java.transaction.dirtyread;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DirtyReadExample {
/**
* @param args
* @throws ClassNotFoundException
* @throws SQLException
* @throws InterruptedException
*/
public static void main(String[] args) throws ClassNotFoundException, SQLException, InterruptedException {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connectionPayment = DriverManager.getConnection(
"jdbc:oracle:thin:@localhost:1521:xe", "hr",
"hr");
Connection connectionReader = DriverManager.getConnection(
"jdbc:oracle:thin:@localhost:1521:xe", "hr",
"hr");
try {
connectionPayment.setAutoCommit(false);
connectionPayment.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
} catch (SQLException e) {
e.printStackTrace();
}
Thread pymtThread=new Thread(new PaymentRunImpl(connectionPayment));
Thread readerThread=new Thread(new ReaderRunImpl(connectionReader));
pymtThread.start();
Thread.sleep(2000);
readerThread.start();
}
}
package com.java.transaction.dirtyread;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class ReaderRunImpl implements Runnable{
private Connection conn;
private static final String QUERY="Select acctBalance from AccountBalance where id=1";
public ReaderRunImpl(Connection conn){
this.conn=conn;
}
@Override
public void run() {
PreparedStatement stmt =null;
ResultSet rs =null;
try {
stmt = conn.prepareStatement(QUERY);
System.out.println("In Reader thread --->Statement Prepared");
rs = stmt.executeQuery();
System.out.println("In Reader thread --->executing");
while (rs.next()){
System.out.println("Balance is:" + rs.getDouble(1));
}
System.out.println("In Reader thread --->Statement Prepared");
Thread.sleep(5000);
stmt.close();
rs.close();
stmt = conn.prepareStatement(QUERY);
rs = stmt.executeQuery();
System.out.println("In Reader thread --->executing");
while (rs.next()){
System.out.println("Balance is:" + rs.getDouble(1));
}
stmt.close();
rs.close();
stmt = conn.prepareStatement(QUERY);
rs = stmt.executeQuery();
System.out.println("In Reader thread --->executing");
while (rs.next()){
System.out.println("Balance is:" + rs.getDouble(1));
}
} catch (SQLException | InterruptedException e) {
e.printStackTrace();
}finally{
try {
stmt.close();
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.java.transaction.dirtyread;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class PaymentRunImpl implements Runnable{
private Connection conn;
private static final String QUERY1="Update AccountBalance set acctBalance=40000 where id=1";
private static final String QUERY2="Update AccountBalance set acctBalance=30000 where id=1";
private static final String QUERY3="Update AccountBalance set acctBalance=20000 where id=1";
private static final String QUERY4="Update AccountBalance set acctBalance=10000 where id=1";
public PaymentRunImpl(Connection conn){
this.conn=conn;
}
@Override
public void run() {
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(QUERY1);
stmt.execute();
System.out.println("In Payment thread --> executed");
Thread.sleep(3000);
stmt = conn.prepareStatement(QUERY2);
stmt.execute();
System.out.println("In Payment thread --> executed");
Thread.sleep(3000);
stmt = conn.prepareStatement(QUERY3);
stmt.execute();
System.out.println("In Payment thread --> executed");
stmt = conn.prepareStatement(QUERY4);
stmt.execute();
System.out.println("In Payment thread --> executed");
Thread.sleep(5000);
//case 1
conn.rollback();
System.out.println("In Payment thread --> rollback");
//case 2
//conn.commit();
// System.out.println("In Payment thread --> commit");
} catch (SQLException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Output:-
In Payment thread --> executed
In Reader thread --->Statement Prepared
In Reader thread --->executing
Balance is:50000.0
In Reader thread --->Statement Prepared
In Payment thread --> executed
In Payment thread --> executed
In Payment thread --> executed
In Reader thread --->executing
Balance is:50000.0
In Reader thread --->executing
Balance is:50000.0
In Payment thread --> rollback
U 可以通过插入由 oracle 定义的新行来测试它:- 当事务 A 检索一组满足给定条件的行时发生幻读,事务 B 随后插入或更新一行,使得该行现在满足事务 A 中的条件, 事务 A 稍后重复条件检索。事务 A 现在看到一个额外的行。该行称为幻像。它将避免上述情况以及我使用了 TRANSACTION_SERIALIZABLE。它将对 Oracle 设置最严格的锁定。Oracle 仅支持 2 种类型的事务隔离级别:- TRANSACTION_READ_COMMITTED 和 TRANSACTION_SERIALIZABLE。