How can I make a row being read wait until a lock on it is released using JDBC and MySQL?

醉酒当歌 提交于 2019-12-23 05:07:26

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!