Spring JDBC连接池和InputStream结果

时间:2009-08-07 17:36:30

标签: java oracle spring jdbc

我正在编写一个Web服务,允许用户发布文件,然后在URL上检索它们(基本上将其视为RESTful Amazon S3)。我遇到的问题是从我的Oracle查询返回一个byte [](Spring JDBC)我返回一个InputStream,然后以块的形式将数据流回客户端。这个(IMO)是一个更好的主意,因为我对文件没有任何大小限制,我不希望内存中有2GB字节数组。

起初它看起来工作正常,但是我在重负载期间遇到了一个案例,有时在上一个servlet可以发送文件之前,Connection会被重用。似乎在返回InputStream的JDBC调用之后,Connection将返回到池(Spring将调用conn.close(),但不清除关联的ResultSet)。因此,如果没有给出Connection的其他请求,则InputStream仍然有效并且可以从中读取,但是如果Connection被赋予新请求,那么InputStream将为null并且先前的请求将失败。

我的解决方案是创建一个InputStream的子类,它也将Connection作为构造函数arg,并且在重写的 public close()方法中也关闭Connection。我不得不抛弃Spring JDBC并只进行正常的PreparedStatement调用,否则Spring总会返回到池的连接。

public class ConnectionInputStream extends InputStream {

   private Connection conn;
   private InputStream stream;

   public ConnectionInputStream(InputStream s, Connection c) {
      conn = c;
      stream = s;
   }

   // all InputStream methods call the same method on the variable stream

   @Override
   public void close() throws IOException {
      try {
         stream.close();
      } catch (IOException ioex) {
          //do something
      } finally {
         try {
             conn.close();
         } catch (SQLException sqlex) {
             //ignore
         }
      }
   }
} 

有没有人有更优雅的解决方案,或者看到我的解决方案有任何明显的问题?此代码也没有从我的实际代码中剪切/粘贴,所以如果有拼写错误就忽略它。

3 个答案:

答案 0 :(得分:3)

不幸的是,当你问这个问题时,我的想象力很疯狂。我不知道这个解决方案是否更优雅。但是,这些类很简单且易于重复使用,因此如果它们不令人满意,您可能会发现它们的用途。你会看到最后一切都在一起......

public class BinaryCloseable implements Closeable {

    private Closeable first;
    private Closeable last;

    public BinaryCloseable(Closeable first, Closeable last) {
        this.first = first;
        this.last = last;
    }

    @Override
    public void close() throws IOException {
        try {
            first.close();
        } finally {
            last.close();
        }
    }

}
BinaryCloseable使用

CompositeCloseable

public class CompositeCloseable implements Closeable {

    private Closeable target;

    public CompositeCloseable(Closeable... closeables) {
        target = new Closeable() { public void close(){} };
        for (Closeable closeable : closeables) {
            target = new BinaryCloseable(target, closeable);
        }
    }

    @Override
    public void close() throws IOException {
        target.close();
    }

}

ResultSetCloser关闭ResultSet个对象:

public class ResultSetCloser implements Closeable {

    private ResultSet resultSet;

    public ResultSetCloser(ResultSet resultSet) {
        this.resultSet = resultSet;
    }

    @Override
    public void close() throws IOException {
        try {
            resultSet.close();
        } catch (SQLException e) {
            throw new IOException("Exception encountered while closing result set", e);
        }
    }

}

PreparedStatementCloser关闭PreparedStatement个对象:

public class PreparedStatementCloser implements Closeable {

    private PreparedStatement preparedStatement;

    public PreparedStatementCloser(PreparedStatement preparedStatement) {
        this.preparedStatement = preparedStatement;
    }

    @Override
    public void close() throws IOException {
        try {
            preparedStatement.close();
        } catch (SQLException e) {
            throw new IOException("Exception encountered while closing prepared statement", e);
        }
    }

}

ConnectionCloser关闭Connection个对象:

public class ConnectionCloser implements Closeable {

    private Connection connection;

    public ConnectionCloser(Connection connection) {
        this.connection = connection;
    }

    @Override
    public void close() throws IOException {
        try {
            connection.close();
        } catch (SQLException e) {
            throw new IOException("Exception encountered while closing connection", e);
        }
    }

}

我们现在将您原来的InputStream想法重构为:

public class ClosingInputStream extends InputStream {

    private InputStream stream;
    private Closeable closer;

    public ClosingInputStream(InputStream stream, Closeable closer) {
        this.stream = stream;
        this.closer = closer;
    }

    // The other InputStream methods...

    @Override
    public void close() throws IOException {
        closer.close();
    }

}

最后,它们汇集在一起​​:

new ClosingInputStream(
        stream,
        new CompositeCloseable(
                stream,
                new ResultSetCloser(resultSet),
                new PreparedStatementCloser(statement),
                new ConnectionCloser(connection)
            )
    );

当调用此ClosingInputStream的{​​{1}}方法时,实际上会发生这种情况(为清楚起见省略了异常处理):

close()

您现在可以随意关闭尽可能多的public void close() { try { try { try { try { // This is empty due to the first line in `CompositeCloseable`'s constructor } finally { stream.close(); } } finally { resultSet.close(); } } finally { preparedStatement.close(); } } finally { connection.close(); } } 个对象。

答案 1 :(得分:0)

在自己发布查询之前,为什么不从查询中读取整个InputStream / byte[] /其他内容?在您的代码告诉Spring /池完成连接后,听起来您正试图从查询中返回数据。

答案 2 :(得分:0)

另一种方法是使用回调。以下是这个想法。

class MyDao
{
   public boolean getData(Function<InputStream, Boolean> processData) {
      // Do your SQL stuff to get a ResultSet
      InputStream input = resultSet.getBinaryStream(0);
      processData.apply(input);
      // Do your cleanup if any
   }
}