code: @Override
    public void close() throws SQLException {
      lock.lock();
      try {
        closed = true;
        if (results != null) {
          if (results.getFetchSize() != 0) {
            skipMoreResults();
          }
          results.close();
        }
  
        // No possible future use for the cached results, so these can be cleared
        // This makes the cache eligible for garbage collection earlier if the statement is not
        // immediately garbage collected
        if (protocol != null) {
          try {
            serverPrepareResult.getUnProxiedProtocol().releasePrepareStatement(serverPrepareResult);
          } catch (SQLException e) {
            //if (log.isDebugEnabled()) log.debug("Error releasing preparedStatement", e);
          }
        }
  
        protocol = null;
        if (connection == null || connection.pooledConnection == null
            || connection.pooledConnection.noStmtEventListeners()) {
          return;
        }
        connection.pooledConnection.fireStatementClosed(this);
        connection = null;
      } finally {
        lock.unlock();
      }
    }
docs: <p>Releases this <code>Statement</code> object's database and JDBC resources immediately
  instead of waiting for this to happen when it is automatically closed. It is generally good
  practice to release resources as soon as you are finished with them to avoid tying up database
  resources.</p>
  <p>Calling the method <code>close</code> on a <code>Statement</code> object that is already
  closed has no effect.</p>
  <p><B>Note:</B>When a <code>Statement</code> object is closed, its current
  <code>ResultSet</code> object, if one
  exists, is also closed.</p>
  
  @throws SQLException if a database access error occurs