如何使用JDBC和MySQL释放行,等待它被释放?

时间:2017-06-12 14:17:54

标签: java mysql jdbc transactions

所以我有这个小例子..

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
    }
}

当我执行这一小段代码时,它会打印出大约350~400毫秒的值,这很好。

现在,当我第一次启动以下代码时..

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();
        }
    }
}

然后运行Bar.java,我会得到大约12-13秒的值,这意味着当Foo.java"锁定" lastname =' tugay',Bar.java等待的行,然后将first_name设置为' bar'。

如果Bar.java尝试读取last_name =' tugay'的行,我想得到相同的行为。所以这就是我的代码:

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));
    }
}

鉴于,初始值first_name是" koray"在数据库中,当我启动Foo.java并且它正在睡觉时#34;当我运行Bar.java时,我会得到:

koray
Took: 390

有没有办法让Bar.java在阅读时等待,就像它在更新时等待一样?

2 个答案:

答案 0 :(得分:1)

您需要locking read

如果您不打算更新该行但只想确保该行上没有其他事务处理,请获取Intention Shared(IS)锁:

SELECT first_name FROM actor WHERE last_name = 'tugay' LOCK IN SHARE MODE

如果您打算在结果返回后更新该行,请获取Intention eXclusive(IX)锁:

SELECT first_name FROM actor WHERE last_name = 'tugay' FOR UPDATE

这两个查询都会阻塞,直到请求的锁定可用。由于ISIX锁与正在执行或已对行执行更新(并且尚未提交或回滚)的事务所持有的独占X锁不兼容,上述任何一个查询都会阻塞,直到其他事务通过提交或回滚来释放其X锁。

此事务才会获得锁并接收其结果。

最后,此事务最终通过提交或回滚来释放它获得的锁。

另见https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-shared-exclusive-locks

答案 1 :(得分:0)

您应该使用事务隔离级别 - TRANSACTION_REPEATABLE_READ。

使用Connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ)

请参阅here