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

时间:2017-05-26 15:26:17

标签: java sql-server oracle jdbc hsqldb

我正在通过JDBC测试一些数据库驱动程序。其中一个测试包括测试db的事务功能:

  1. 我打开两个连接到同一个db(autoCommit = false)。
  2. 在连接A上,我在表中插入一行而不执行提交。
  3. 在连接B上,我希望不会看到那一行。
  4. 在连接A上,我执行提交。
  5. 在连接B上,我希望看到那一行。
  6. 该测试适用于Oracle 12,但对于其他数据库(如HSQLDB,Derby或SQL Server),:它在步骤3的中间阻塞。

    我猜原因可能与事务隔离参数有关,但我在创建两个连接时都尝试了所有可能的值,结果总是相同。

    为什么阻止?为什么Oracle没有阻止?

    这是我的代码:

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

    我的依赖项:

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

    提前致谢。

3 个答案:

答案 0 :(得分:4)

在Oracle中,数据库读者永远不会等待编写者,除非可能出现异常情况,您基本上可以忽略这些情况。出于所有实际目的,读者永远不会等待作家。

在其他数据库中,情况并非总是如此。例如,在HSQLDB中,默认锁定模式是2PL(两阶段锁定)。在该模型中,写入表会获取该表的独占锁定,从而阻止其他会话读取该表。它具有更复杂的锁定模型(例如,MVCC),可以使用SET DATABASE TRANSACTION CONTROL命令进行设置。

这是一个很好的例子,说明为什么“数据库独立性”真的很难(对于我的钱,我从来没有把目标定为目标)。

答案 1 :(得分:1)

在这些数据库系统中实现隔离的方式不同,请点击此链接获取更多信息:

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

简而言之,Oracle以一种编写器不会阻止读者的方式实现它,但对于其他RDBMS则不然。

答案 2 :(得分:0)

如果您将HSQLDB作为服务器运行并使用MVCC,您将获得预期的结果。

当您在进程中使用HSQLDB时,必须为每个连接使用单独的线程,并使MVCC使其工作。