簡體   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