[英]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)
)
);
當ClosingInputStream
此ClosingInputStream
的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();
}
}
現在,您可以自由關閉盡可能多的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.