How can I see TRANSACTION_REPEATABLE_READ in actio

2019-07-13 08:22发布

问题:

I have MySQL Server (5.7.16) running on my computer currently, and have this sample row in a database called 'Sakila' on this Server:

mysql> SELECT * FROM actor WHERE last_name = 'tugay';
+----------+------------+-----------+---------------------+
| actor_id | first_name | last_name | last_update         |
+----------+------------+-----------+---------------------+
|      201 | koray      | tugay     | 2017-06-11 21:42:08 |
+----------+------------+-----------+---------------------+

This is what JDBC returns for TRANSACTION ISOLATION LEVEL for my MySQL Server:

public static void main(String[] args) throws SQLException, InterruptedException {
    final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
    System.out.println(connection.getTransactionIsolation()); // Prints 4!
    connection.close();
}

where 4 here is the constant for TRANSACTION_REPEATABLE_READ which is documented as follows:

int TRANSACTION_REPEATABLE_READ  = 4;
A constant indicating that dirty reads are prevented. 
This level prohibits a transaction from reading a row with uncommitted changes in it.

I want to see this behavior so I have 2 main methods, first being:

import java.sql.*;

import static java.sql.DriverManager.*;

public class Foo {

    public static void main(String[] args) throws SQLException, InterruptedException {
        final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
        connection.setAutoCommit(false);

        final Statement statement = connection.createStatement();
        statement.executeUpdate("UPDATE actor SET first_name = 'ALTERED' WHERE first_name = 'koray'");

        System.out.println("Sleeping!");

        // Here I am expecting MySQL to lock the Row so no other Connection
        // can read data from it. This row will be updated, so unless the transaction is 
        // either commited or rolled back, the read data will be 'dirty'.
        Thread.sleep(5000);

        connection.commit();
        connection.close();
    }
}

As you see, the transaction is not in auto-commit mode, and I am locking the row for 5 seconds. While this process is sleeping, I constantly run the following code as well:

public class Bar {

    public static void main(String[] args) throws SQLException, InterruptedException {
        final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
        connection.setAutoCommit(false);

        final Statement statement = connection.createStatement();
        final ResultSet resultSet = statement.executeQuery("SELECT * FROM actor WHERE last_name = 'tugay'");

        resultSet.next();
        System.out.println(resultSet.getString("first_name"));

        connection.close();
    }
}

But even I see "sleeping" text in the console, as long as I run the main method in Bar, I will see values like

koray
koray
koray
ALTERED

My expectation was never to see koray since that is a "dirty data", where the first java process should be locking the row.

What am I missing and how can I lock this row?

回答1:

You should read the definition again:

This level prohibits a transaction from reading a row with uncommitted changes in it.

ALTERED is the dirty data, since it's not commited. Koray is the correct one. Bar is prohibited to see "ALTERED". You'll keep seeing "koray" as long as the transaction wasn't commited.