【问题标题】:DBCP - PSQL Exception - Connection PoolDBCP - PSQL 异常 - 连接池
【发布时间】:2017-04-19 23:49:42
【问题描述】:

我的单元测试有问题。我正在尝试实现一些单元测试来检查数据库请求(插入、更新、解析、绑定、执行......)。

事情是我得到以下异常:

PSQLException : 此连接已关闭。

我的代码:

DataSource 类可以在不同的类中使用相同的连接

public class DataSource {

    private static DataSource     datasource;
    private BasicDataSource ds;

    private DataSource() throws IOException, SQLException, PropertyVetoException {
        ds = new BasicDataSource();
        ds.setDriverClassName("org.postgresql.Driver");
        ds.setUsername("postgres");
        ds.setPassword("postgres");
        ds.setUrl("jdbc:postgresql://localhost:5432/ehealth");

     // the settings below are optional -- dbcp can work with defaults
     //   ds.setMinIdle(5);
     //   ds.setMaxIdle(20);
     //   ds.setMaxOpenPreparedStatements(180);

    }

    public static DataSource getInstance() throws IOException, SQLException, PropertyVetoException {
        if (datasource == null) {
            datasource = new DataSource();
            return datasource;
        } else {
            return datasource;
        }
    }

    public Connection getConnection() throws SQLException {
        return this.ds.getConnection();
    }

}

我的静态方法返回<String, List<PreparedStatement>>的映射

public static Map<String, List<PreparedStatement>> test(int numberOfRequest) throws SQLException{
        try(Connection con = getHealthConnection(); Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 
                ResultSet.CONCUR_READ_ONLY, 
                ResultSet.HOLD_CURSORS_OVER_COMMIT);){
            Map<String, List<PreparedStatement>> myMap = new HashMap<String, List<PreparedStatement>>();
            List<PreparedStatement> lstPreparedStmt = new ArrayList<PreparedStatement>();
            String request = "INSERT INTO PATIENT VALUES (?, ?, ?, ?, 'M', 'OTHER')";
            myMap.put(request, null);
            int counter = 0;
            String rqstSelect = "SELECT * FROM patient ORDER BY pat_id"; //initialize SELECT request
            ResultSet resRequest = stmt.executeQuery(rqstSelect); //execute SELECT
            String newId = createNewIdByIncrement(resRequest); //initialize first new id
            while(counter < numberOfRequest){
                String newName = generateRandomString(); //generate random new name
                String newLast = generateRandomString(); //generate random new last name
                String newLast2 = generateRandomString(); //generate random new last name 2
                PreparedStatement prep = con.prepareStatement(request, ResultSet.TYPE_SCROLL_INSENSITIVE,
                        ResultSet.CONCUR_READ_ONLY,
                        ResultSet.HOLD_CURSORS_OVER_COMMIT);
                prep.setString(1, newId);
                prep.setString(2, newName);
                prep.setString(3, newLast);
                prep.setString(4, newLast2);
                lstPreparedStmt.add(prep);
                counter = counter + 1; //increment counter
                int newIdAsInt = Integer.parseInt(newId) + 1; //increment id
                newId = concatenationZeroIntAsString(newIdAsInt, 8);
            }
            myMap.put(request, lstPreparedStmt);
            return myMap;
        }
    }

最后,由我的@Test 调用的方法共享与先前方法调用的相同连接

private void insertPreparedStatementRequest(int numberOfRequest) throws SQLException, IOException, PropertyVetoException{
        try(Connection con = DataSource.getInstance().getConnection(); Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, 
                ResultSet.CONCUR_READ_ONLY, 
                ResultSet.HOLD_CURSORS_OVER_COMMIT);){    
            // check number of row before insert
            String rqstCountRowBefore = "SELECT * FROM patient ORDER BY pat_id";
            ResultSet resCheckBefore = stmt.executeQuery(rqstCountRowBefore);
            int rowBefore = TestUtils.getRowCount(resCheckBefore);
            Assert.assertNotNull("Request's result is null", resCheckBefore);
            // insert X row
            Map<String, List<PreparedStatement>> allRequest = TestUtils.test(numberOfRequest);
            for(Map.Entry<String, List<PreparedStatement>> e : allRequest.entrySet()){
                List<PreparedStatement> lstStatement = e.getValue();
                Iterator<PreparedStatement> valIterator = lstStatement.iterator();
                while(valIterator.hasNext()){
                    valIterator.next().execute();
                }
            }
            // check number of row after insert
            String rqstCountrowAfter = "SELECT * FROM patient ORDER BY pat_id";
            ResultSet resCheckAfter = stmt.executeQuery(rqstCountrowAfter);
            int rowAfter = TestUtils.getRowCount(resCheckAfter);
            Assert.assertNotNull("Request's result is null", resCheckBefore);
            Assert.assertEquals("Insert result's request unsuccesfull", rowBefore + numberOfRequest, rowAfter);
        }
    }

我不明白为什么我的连接似乎已关闭。我需要在最后一种方法中获得与第一种方法相同的连接。问题是我创建了一个 DataSource 来使用第一种方法中已经创建的连接。

错误来自这一行:valIterator.next().execute()

日志:

org.postgresql.util.PSQLException: This connection has been closed.
    at org.postgresql.jdbc2.AbstractJdbc2Connection.checkClosed(AbstractJdbc2Connection.java:714)
    at org.postgresql.jdbc2.AbstractJdbc2Connection.getAutoCommit(AbstractJdbc2Connection.java:680)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:507)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:381)
    at eu.clarussecure.proxy.protocol.plugins.pgsql.Scenarii.insertPreparedStatementRequest(Scenarii.java:191)
    at eu.clarussecure.proxy.protocol.plugins.pgsql.Scenarii.insertPreparedStmtPatientRow5(Scenarii.java:109)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

【问题讨论】:

  • 每次测试后如何清理数据库中的数据? createNewIdByIncrement() 函数究竟做了什么?
  • 实际上没有办法在测试后清理数据库。我稍后会编码(不知道如何)。我的客户已经得到了一个数据库,我正在研究它。如果他将 id 列设置为自动增量,他现在不会。然后,以防万一,我已经实现了设置 Id 的方法(以实现我的测试),即使我确定它会自动递增
  • 盲注:createNewIdByIncrement() 在每次运行时创建相同的 Id,但您的表已经将这些值作为主键。 INSERT 失败,try(Connection con = ...) { ... } 关闭连接。然后您会看到连接在其他地方关闭,但它与明显的故障点无关,因为连接之前已关闭。
  • 我在其他几个测试中使用 createNewIdByIncrement() 并且工作正常。不要这么想,这就是问题所在。我试图只创建一种方法并将所有方法 test() 代码放在 insertPreparedStatement() 方法中。一切都运作良好。然后问题来自连接。我需要创建一个连接池(我相信)。我不知道如何将 Name 配置为 bind() 和 lookup() 我的数据源。我正在使用 Postgres,我可能需要创建一个属性文件......我是连接池的初学者

标签: java postgresql unit-testing jdbc connection-pooling


【解决方案1】:

PreparedStatement 是特定Connection 上下文中的资源。在您的test 方法中,您已经从-Connection con = getHealthConnection(); 创建了准备好的语句,然后您退出方法-test。

退出test 方法将返回连接到池或关闭它,但最终你将无法获得相同的Connection 对象,除非你在Connection 上实现了单例,而你很可能不会Connection在对 test 的调用完成后,将关闭创建语句。

在您的 insertPreparedStatementRequest 方法中,您正在创建一个新的 Connection 并且该连接对象与准备这些语句的连接对象无关,因此会出现错误。

只要这些语句的底层连接仍然存在,您就需要执行这些准备好的语句。为此,您需要将 Connection 从您的 insertPreparedStatementRequest 传递到 test 方法,即需要将 Connection 设置为 test 方法的外部。

希望对你有帮助!!

【讨论】:

  • 喂!现在看起来真的很简单!而不是创建连接(在 test() 结束时关闭),我可以将这两种方法放在另一个方法中(启动连接)并添加连接参数。会试试这个,非常感谢你的队友!
猜你喜欢
  • 2019-03-03
  • 2012-12-22
  • 2019-10-18
  • 1970-01-01
  • 1970-01-01
  • 2012-07-03
  • 2015-03-28
  • 2013-01-06
相关资源
最近更新 更多