# JDBC查詢資料時出現「Out of Memory」？

> ## 注意：
>
> 其他程式語言或資料庫也可能會有這個問題，請查閱相對應的驅動程式手冊。

簡單來說：

JDBC 在查詢資料時，預設會取回所有的資料，所以如果你的資料量過大，且AP端的資源不足時，就可能會產生這個錯誤。許多人以為 next() 就是在使用 cursor，其實不然。

詳細說明請參閱 [PostgreSQL JDBC 使用手冊第 5 章](https://jdbc.postgresql.org/documentation/head/query.html)的內容。以下為該章的編譯內容：

任何時候你想要將 SQL 查詢語句送到資料庫，你都需要一個 Statement 或 PreparedStatement。一旦你有一個 Statement 或 PreparedStatement 了，你可以就可以開始查詢。這將回傳一個 ResultSet 的實例，該實例包含全部的資料結果（請參閱「[以游標（cursor）取得結果](/jdbc-cha-xun-zi-liao-shi-chu-xian-out-of-memory.md#cursor)」一節以了解如何變更此行為）。 範例 1，「[在 JDBC 中進行一個簡單的查詢](/jdbc-cha-xun-zi-liao-shi-chu-xian-out-of-memory.md#ex1)」說明了這個過程。

### 範例 1. 在 JDBC 中進行一個簡單的查詢 <a href="#ex1" id="ex1"></a>

這個範例將送出一個簡單的查詢並使用一個 Statement 來輸出每一個資料列的第一個欄位：

```
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE columnfoo = 500");
while(rs.next()){
    System.out.print("Column 1 returned ");
    System.out.println(rs.getString(1));
}
rs.close();
st.close();
```

此範例與之前一樣送出相同的查詢，但在查詢中使用 PreparedStatement 和設定值：

```
int foovalue = 500;
PreparedStatement st = conn.prepareStatement("SELECT * FROM mytable WHERE columnfoo = ?");
st.setInt(1,foovalue);
ResultSet rs = st.executeQuery();
while(rs.next()) {
    System.out.print("Column 1 returned ");
    System.out.println(rs.getString(1));
}
rs.close();
st.close();
```

## 以游標（cursor）取得結果 <a href="#cursor" id="cursor"></a>

預設的情況下，驅動程式會一次取回查詢的所有結果。這對於大數據來說可能會不方便，因此 JDBC 驅動程式提供了將 ResultSet 放在資料庫游標上的方法，只提取少量的資料列。

少量的資料列會被暫存在連線的用戶端，當資料取完時，再透過重新定位游標來檢索下一群的資料列。

## 注意

> 以游標的 ResultSet 查詢無法在所有情況下使用。有很多限制會讓驅動程式暗自地回到一次取得整個 ResultSet。

* 與伺服器的連線必須使用 V3 協定。這是伺服器 7.4 版或更高版本的設定（並且僅接受 V3）。
* 連線不得處於自動提交（autocommit）模式。後端在交易事務結束時才關閉游標，而在自動提交模式下，後端將在關閉交易之前關閉游標。 \* Statement 必須用 ResultSet型別的 ResultSet.TYPE\_FORWARD\_ONLY。這是預設值，因此不需要重寫程式就能使用，但這也意味著你無法倒退或以其他方式在 ResultSet 中跳轉。
* 送出的查詢必須是單一個查詢語句，而不是以多個分號串在一起的查詢語句。

### 範例 2. 以設定暫存大小來開關游標的使用

將程式更改為游標模式非常簡單，只需將語句的暫存大小設定為適當值即可。將暫存大小設定回 0，就會導致所有資料列被暫存（預設行為）。

```
// make sure autocommit is off
conn.setAutoCommit(false);
Statement st = conn.createStatement();
// Turn use of the cursor on.
st.setFetchSize(50);
ResultSet rs = st.executeQuery("SELECT * FROM mytable");
while(rs.next()) {
    System.out.print("a row was returned.");
}
rs.close();
// Turn the cursor off.
st.setFetchSize(0);
rs = st.executeQuery("SELECT * FROM mytable");
while(rs.next()) {
    System.out.print("many rows were returned.");
}
rs.close();
// Close the statement.
st.close();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://faq.postgresql.tw/jdbc-cha-xun-zi-liao-shi-chu-xian-out-of-memory.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
