繁体   English   中英

Spring JDBC连接池和InputStream结果

[英]Spring JDBC connection pool and InputStream results

我正在编写一个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
         }
      }
   }
} 

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

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

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

ClosingInputStreamClosingInputStreamclose()方法时,实际上会发生这种情况(为清楚起见省略了异常处理):

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

现在,您可以自由关闭尽可能多的Closeable ,只要你喜欢的对象。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM