简体   繁体   English

死锁在同一线程中打开两个JDBC事务

[英]Deadlock opening two JDBC transactions in the same thread

I'm testing some database drivers through JDBC. 我正在通过JDBC测试一些数据库驱动程序。 One of the tests consists on testing the transactional capabilities of the db: 测试之一包括测试数据库的事务功能:

  1. I open two connections to the same db (with autoCommit=false). 我打开到同一数据库的两个连接(使用autoCommit = false)。
  2. On connection A, I insert one row in a table without performing a commit. 在连接A上,我在表中插入一行而不执行提交。
  3. On connection B, I expect not to see that row yet. 在连接B上,我希望看不到该行。
  4. On connection A, I perform a commit. 在连接A上,我执行提交。
  5. On connection B, I expect to see that row. 在连接B上,我希望看到该行。

The test works fine for Oracle 12, but for other databases like HSQLDB, Derby or SQL Server, NO : It blocks in the middle of step 3. 该测试在Oracle 12上运行良好,但在其他数据库(例如HSQLDB,Derby或SQL Server)中,则不会 :在步骤3的中间阻塞。

I guess the cause might be something related to transaction isolation parameter, but I've tried all possible values when creating both connections, with the same result always. 我想原因可能与事务隔离参数有关,但是我在创建两个连接时都尝试了所有可能的值,始终得到相同的结果。

Why does it block? 为什么会阻塞? And why Oracle does no block? 为什么Oracle不阻塞?

This is my code: 这是我的代码:

public class JdbcTest
{
    @Test
    public void twoTransactionsTest()
        throws SQLException,
        IOException
    {
        String tableName="dummy01";
        // 1. I open two connections to the same db (with autoCommit=false).
        try (Connection connectionA=createConnection(); Connection connectionB=createConnection())
        {
            createTable(connectionA, tableName);

            // 2. On connection A, I insert one row in a table without performing a commit.
            execute(connectionA, "INSERT INTO " + tableName + " VALUES(50, 'grecia')");

            // 3. On connection B, I expect not to see that row yet.
            int records=queryAndCountRows(connectionB, "SELECT id FROM " + tableName + " WHERE id=50");
            assertEquals(0, records);

            // 4. On connection A, I perform a commit.
            connectionA.commit();

            // 5. On connection B, I expect to see that row.
            records=queryAndCountRows(connectionB, "SELECT * FROM " + tableName + " WHERE id=50");
            assertEquals(1, records);
            dropTable(connectionA, tableName);
        }
    }

    private Connection createConnection()
        throws SQLException,
        IOException
    {
        String url="jdbc:hsqldb:demo.hsqldb";
        String user="demo";
        String password="";
        Connection connection=DriverManager.getConnection(url, user, password);
        connection.setAutoCommit(false);
        return connection;
    }

    private int queryAndCountRows(Connection connection, String sql)
        throws SQLException
    {
        try (PreparedStatement pst=connection.prepareStatement(sql))
        {
            try (ResultSet rs=pst.executeQuery())
            {
                int records=0;
                while (rs.next())
                {
                    records++;
                }
                return records;
            }
        }
    }

    private void execute(Connection connection, String sql)
        throws SQLException
    {
        try (Statement statement=connection.createStatement())
        {
            statement.execute(sql);
        }
    }

    private void createTable(Connection connection, String tableName)
        throws SQLException
    {
        try
        {
            execute(connection, "DROP TABLE " + tableName);
        }
        catch (SQLException e)
        {
            // If the table already exists, let's ignore this error.
        }
        execute(connection, "CREATE TABLE " + tableName + "(id NUMBER(5) NOT NULL, name VARCHAR2(100))");
    }

    private void dropTable(Connection connection, String tableName)
        throws SQLException
    {
        execute(connection, "DROP TABLE " + tableName);
    }
}

My dependencies: 我的依赖:

<dependency>
  <groupId>org.hsqldb</groupId>
  <artifactId>hsqldb</artifactId>
  <version>2.3.2</version>
</dependency>
<dependency>
  <groupId>com.oracle</groupId>
  <artifactId>ojdbc6</artifactId>
  <version>11.2.0</version>
</dependency>
<dependency>
  <groupId>com.microsoft.sqlserver</groupId>
  <artifactId>mssql-jdbc</artifactId>
  <version>6.1.0.jre8</version>
</dependency>

Thanks in advance. 提前致谢。

In Oracle, database readers never wait for writers, except possibly in extremely unusual cases which you can basically ignore. 在Oracle中,数据库读取器永远不会等待写入器,除非在极端不常见的情况下,您基本上可以忽略它们。 For all practical purposes, readers never wait for writers. 出于所有实际目的,读者永远不会等作家。

In other databases, that is not always the case. 在其他数据库中,情况并非总是如此。 In HSQLDB, for example, the default locking mode is 2PL (two-phase locking). 例如,在HSQLDB中,默认锁定模式为2PL(两阶段锁定)。 In that model, writing to a table acquires an exclusive lock on that table, preventing other sessions from reading that table. 在该模型中,写入表将获得该表的排他锁,从而防止其他会话读取该表。 It has more sophisticated locking models (eg, MVCC), that can be set with the SET DATABASE TRANSACTION CONTROL command. 它具有更复杂的锁定模型(例如MVCC),可以使用SET DATABASE TRANSACTION CONTROL命令进行设置。

This is a good example of why "database independence" is really hard (and for my money, something I never set as a goal). 这是一个很好的例子,说明为什么“数据库独立性”真的很困难(而且,以我的钱,这是我从未设定的目标)。

The isolation is implemented differently in those DB systems, please follow this link for more information: 在这些数据库系统中,隔离的实现方式有所不同,请单击此链接以获取更多信息:

http://www.dba-in-exile.com/2012/11/isolation-levels-in-oracle-vs-sql-server.html http://www.dba-in-exile.com/2012/11/isolation-levels-in-oracle-vs-sql-server.html

In short, Oracle implements it in a way that writer do not block readers, but the same is not true for other RDBMS. 简而言之,Oracle以编写者不阻止读取者的方式来实现它,但其他RDBMS则不是这样。

You will get the result you expect if you run HSQLDB as a server and use MVCC. 如果将HSQLDB作为服务器运行并使用MVCC,则将获得期望的结果。

When you use HSQLDB in-process, you must use a separate thread for each connection and MVCC for it to work. 在进程内使用HSQLDB时,必须为每个连接和MVCC使用单独的线程才能使其正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM