PreparedStatement.close PooledConnection problem

233 views
Skip to first unread message

Ryszard Trojnacki

unread,
Mar 26, 2018, 8:53:17 AM3/26/18
to Ebean ORM
Hello,

I'm working with Apache Ignite. For discovery I'm using TcpDiscoveryJdbcIpFinder (searching other nodes via database). Implementation of TcpDiscoveryJdbcIpFinder is:
    @Override public void registerAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
        ...
        try {
            stmtUnreg = conn.prepareStatement(unregAddrQry);
            stmtReg = conn.prepareStatement(regAddrQry);
        ...
            stmtUnreg.close();
        ...
            stmtReg.close();
        ...
        finally {
            U.closeQuiet(stmtUnreg);
            U.closeQuiet(stmtReg);
        }


Problem here is that PreparedStatement.close is called twice what is a bad thing, but legal according to JavaDoc:
https://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#close()
Calling the method close on a Statement object that is already closed has no effect.

Unfortunately Ebean PooledConnection wraps PreparedStatement and close cannot be called more than once. This is because of code:
  /**
   * Return a PreparedStatement back into the cache.
   */
  void returnPreparedStatement(ExtendedPreparedStatement pstmt) {

    synchronized (pstmtMonitor) {
      if (!pstmtCache.returnStatement(pstmt)) {
        try {
          // Already an entry in the cache with the exact same SQL...
          pstmt.closeDestroy();

        } catch (SQLException e) {
          logger.error("Error closing Pstmt", e);
        }
      }
    }
  }


First time PreparedStatement is closed it is returned to pstmtCache (pstmtCache.returnStatement(pstmt) -> true).
Second time it is already in cache (pstmtCache.returnStatement(pstmt) -> false) and therefor it is closed (pstmt.closeDestroy()).
When it is used next time then it is returened from cache (first call) but it is closed, because of the second call and I got runtime exception:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The statement is closed.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:191)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.checkClosed(SQLServerStatement.java:1073)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.setString(SQLServerPreparedStatement.java:1577)
at org.avaje.datasource.pool.ExtendedPreparedStatement.setString(ExtendedPreparedStatement.java:326)
at org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder.unregisterAddresses(TcpDiscoveryJdbcIpFinder.java:235)
... 1 more


I know that Apache Ignite TcpDiscoveryJdbcIpFinder code is bad, but it is correct with definition of PreparedStatement.close.

To solve this you should propably add a field closed to ExtendedPreparedStatement and check it in ExtendedPreparedStatement.close method:
  public void close() throws SQLException {
    if(closed) return;  // Calling the method close on a Statement object that is already closed has no effect.
    closed=true;
    pooledConnection.returnPreparedStatement(this);
  }


-- 
Greetings,
Ryszard Trojnacki

Rob Bygrave

unread,
Mar 26, 2018, 5:04:41 PM3/26/18
to ebean@googlegroups
Fixed in avaje-datasource-3.2.3 ... which is heading to maven central now.


--

---
You received this message because you are subscribed to the Google Groups "Ebean ORM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages