问题
So I have this small example..
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import static java.sql.DriverManager.getConnection;
public class Bar {
public static void main(String[] args) throws SQLException, InterruptedException {
final long start = System.currentTimeMillis();
final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
final Statement statement = connection.createStatement();
statement.executeUpdate("UPDATE actor SET first_name = 'bar' WHERE last_name = 'tugay'");
statement.close();
connection.close();
final long end = System.currentTimeMillis();
System.out.println("Took: " + (end - start)); // Will print around 350ms
}
}
and when I execute this small piece of code, it will print out values around 350 ~ 400 milliseconds, which is fine.
Now when I first start the following code..
import java.sql.*;
import static java.sql.DriverManager.*;
public class Foo {
public static void main(String[] args) {
final Connection connection;
try {
connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
connection.setAutoCommit(false);
final Statement statement = connection.createStatement();
statement.executeUpdate("UPDATE actor SET first_name = 'foo' WHERE last_name = 'tugay'");
System.out.println("Sleeping!");
Thread.sleep(15000); // Sleep for 15 seconds..
connection.commit();
connection.close();
} catch (SQLException | InterruptedException e) {
e.printStackTrace();
}
}
}
and then run Bar.java, I will get values around 12 - 13 seconds, which means while Foo.java "locks" the rows where lastname = 'tugay', Bar.java just waits, and then sets the first_name to 'bar'.
I would like to get same behavior if Bar.java tries to read rows where last_name = 'tugay'. So this is the code I have:
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import static java.sql.DriverManager.getConnection;
public class Bar {
public static void main(String[] args) throws SQLException, InterruptedException {
final long start = System.currentTimeMillis();
final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
final Statement statement = connection.createStatement();
final ResultSet resultSet = statement.executeQuery("SELECT first_name FROM actor WHERE last_name = 'tugay'");
resultSet.next();
System.out.println(resultSet.getString("first_name"));
statement.close();
connection.close();
final long end = System.currentTimeMillis();
System.out.println("Took: " + (end - start));
}
}
Given, initally the value first_name is "koray" in the database, when I start Foo.java and while it "sleeps", when I run Bar.java I will get:
koray
Took: 390
Is there a way to make Bar.java wait while reading as well, just like it waits when updating?
回答1:
You need a locking read.
If you do not intend to update the row but merely want to ensure that no other transaction is working on the row, obtain an Intention Shared (IS
) lock:
SELECT first_name FROM actor WHERE last_name = 'tugay' LOCK IN SHARE MODE
If you intend to update the row after the result comes back, obtain an Intention eXclusive (IX
) lock:
SELECT first_name FROM actor WHERE last_name = 'tugay' FOR UPDATE
Both of these queries will block until the requested lock is available. Since IS
and IX
locks are incompatible with the exclusive X
lock held by the transaction that is doing or has done an update on the row (and has not yet committed or rolled back), either of the above queries will block until that other transaction releases its X
lock by either committing or rolling back.
Only then does this transaction obtain the locks and receive its result.
Finally, this transaction eventually releases the locks it obtained, by committing or rolling back.
See also https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-shared-exclusive-locks
回答2:
You should use transaction isolation level - TRANSACTION_REPEATABLE_READ.
Use Connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ)
See here
来源:https://stackoverflow.com/questions/44501681/how-can-i-make-a-row-being-read-wait-until-a-lock-on-it-is-released-using-jdbc-a