연결이 닫힌 후에도 JDBC 결과 세트와 명령문을 별도로 닫아야합니까?


256

사용 후 모든 JDBC 자원을 닫는 것이 좋은 습관이라고합니다. 그러나 다음 코드가있는 경우 결과 집합과 진술을 닫아야합니까?

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    try { if (rs != null) rs.close(); } catch (Exception e) {};
    try { if (stmt != null) stmt.close(); } catch (Exception e) {};
    try { if (conn != null) conn.close(); } catch (Exception e) {};
}

문제는 연결 종료가 작업을 수행하는지 또는 일부 리소스를 사용 중인지 여부입니다.


답변:


199

당신이 한 일은 완벽하고 아주 좋은 연습입니다.

내가 모범 사례를 말하는 이유 ... 예를 들어 어떤 이유로 "기본"유형의 데이터베이스 풀링을 사용하고을 호출 connection.close()하면 연결이 풀로 반환되고 ResultSet/Statement 는 절대 닫히지 않습니다. 여러 가지 새로운 문제가 발생합니다!

그래서 항상 믿을 수는 없습니다 connection.close() 정리할 .

이게 도움이 되길 바란다 :)


4
... 그리고 모든 것을 명시 적으로 닫아야하는 가장 분명한 이유입니다.
Zeemee

2
결과 집합과 설명을 닫는 것이 좋습니다. 그러나 결과 집합과 명령문은 가비지 수집됩니다. 이들은 영원히 열려 있지 않으며 "다른 많은 새로운 문제에 부딪치지"않습니다.
stepanian

3
@Ralph Stevens-당신은 그것을 믿을 수 없습니다. 가비지 수집 후에도 ResultSet이 닫히지 않아서 MSSQL JDBC 드라이버가 메모리를 유출 한 상황이있었습니다.
Paul

7
@Paul-흥미 롭습니다. 그것은 JDBC 드라이버의 단점처럼 들립니다.
stepanian

2
@tleb-예상대로 작동합니다. 이론 상으로는 예외가 "비싸다"고하여 아주 작은 성능 저하가있을 수 있습니다 (이미 확인한 바 있음)
Paul

124

Java 1.7은 try-with-resources 선언문 덕분에 우리의 삶을 훨씬 쉽게 만듭니다 .

try (Connection connection = dataSource.getConnection();
    Statement statement = connection.createStatement()) {
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do stuff with the result set.
    }
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do more stuff with the second result set.
    }
}

이 구문은 매우 간단하고 우아합니다. 그리고 그것을 만들 수 없을 connection때에도 실제로 닫힙니다 statement.


56
이와 같이 중첩 할 필요는 없으며, 한 번의 리소스 사용으로 모든 작업을 수행 할 수 있으며, 리소스 선언을 별도의 문 (으로 구분 ;) 으로 취급하면됩니다.
Mark Rotteveel

2
Mark Rotteveel : 세 가지 Connection, Statement 및 ResultSet 모두에 대해 단일 시도를 사용할 수 있지만 여러 쿼리를 수행하려면 새 쿼리를 시작하기 전에 이전 ResultSet을 닫아야합니다. 적어도 그것이 내가 사용했던 DBMS가 작동하는 방식입니다.
Raúl Salinas-Monteagudo

왜 이런 짓을하지 않습니까? try (open connection) {try (여러 명령문 및 결과 집합) {특히 다음 쿼리 결과가 이전 결과로 계산할 수있는 경우.
다니엘 Hajduk

Daniel :이 패턴을 사용할 때 기본 JDBC 백엔드는 ResultSet을 열어 두 번째를 여는 것을 지원하지 않았습니다.
Raúl Salinas-Monteagudo

rascio, 캐치 블록에 필요한 모든 것을 할 수 있습니다
Raúl Salinas-Monteagudo

73

로부터 의 javadoc :

하면 Statement개체가 닫혀, 현재 ResultSet존재하는 경우 목적은 역시 폐쇄된다.

그러나 javadocs는 기초를 닫을 때 StatementResultSet닫혀 있는지 여부에 대해 명확하지 않습니다 Connection. 그들은 단순히 Connection을 닫는다 고 말합니다.

Connection오브젝트의 데이터베이스 및 JDBC 자원을 자동으로 해제하기를 기다리지 않고 즉시 해제합니다.

제 생각에는, 항상 명시 적으로 가까운 ResultSets, Statements그리고 Connections당신은의 구현으로 마친 후 close데이터베이스 드라이버에 따라 다를 수 있습니다.

당신은 같은 방법을 사용하여 자신에게 보일러 플레이트 코드를 많이 절약 할 수 closeQuietly있는 DBUtils 아파치.


1
고마워요 요점은 Connection.close의 구현에 의존 할 수 없다는 것입니다.
Zeemee


39

Java와 함께 Oracle을 사용하고 있습니다. 여기 내 관점 :

당신은 닫아야 ResultSetStatement오라클이 커서를 유지하면서 이전에 문제가 명시 적으로하기 때문에 연결을 종료 후에도 엽니 다. ResultSet(커서를) 닫지 않으면 최대 열린 커서 초과 와 같은 오류가 발생 합니다.

나는 당신이 사용하는 다른 데이터베이스와 같은 문제가 발생할 수 있다고 생각합니다.

다음은 자습서 닫기 ResultSet가 완료된 후입니다 .

완료되면 ResultSet 닫기

닫기 ResultSet빨리 작업을 완료 한만큼 객체 ResultSet에도 불구하고 개체 Statement개체가 종료 ResultSet가 폐쇄 닫을 때 암시 적으로 개체가 ResultSet있기 때문에 명시 적으로 가능한 빨리 배열 회수 메모리에 가비지 컬렉터에 기회를 제공 ResultSet개체가 쿼리에 따라 메모리를 많이 차지할 수있다.

ResultSet.close();


감사합니다. 가능한 빨리 종료해야하는 좋은 이유입니다. 그러나 연결하기 전에 ResultSet 및 Statement가 직접 닫히는 것이 중요합니까? (가능한 경우 초기가 아님)?
Zeemee

연결을 닫으면 모든 결과 집합 ans 문도 닫히지 만 연결 전에 결과 집합을 닫아야합니다.

연결하기 전에 왜 결과 집합을 닫아야합니까? 오라클 드라이버 문제로 인한 것입니까?
Zeemee

1
여기에 더 일반적인 설명이 있습니다 :) stackoverflow.com/questions/103938/…

이론적으로, 당신은 문을 닫을 경우는하지 않는 결과 집합을 닫습니다,하지만 아마 좋은 방법입니다.
rogerdpack

8

더 작은 코드를 원한다면 Apache Commons DbUtils를 사용하는 것이 좋습니다 . 이 경우 :

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(stmt);
    DbUtils.closeQuietly(conn);
}

3
rs.close (), stmt.close (), conn.close () 대신이 코드를 사용하면 어떻게됩니까?
Onkar Musale

3

JDBC와 관련된 자원을 닫는 올 바르고 안전한 방법 ( JDBC 자원을 올바르게 닫는 방법-항상 )

Connection connection = dataSource.getConnection();
try {
    Statement statement = connection.createStatement();

    try {
        ResultSet resultSet = statement.executeQuery("some query");

        try {
            // Do stuff with the result set.
        } finally {
            resultSet.close();
        }
    } finally {
        statement.close();
    }
} finally {
    connection.close();
}

3

상관 없습니다 Connection풀링 가능 는 하지 않습니다. 풀로 돌아 오기 전에 풀링 가능한 연결도 정리해야합니다.

"청소"는 일반적으로 결과 집합을 닫고 보류중인 트랜잭션을 롤백하지만 연결을 닫지 않는 것을 의미합니다. 그렇지 않으면 풀링의 의미가 느슨해집니다.


2

아니요 연결을 닫을 필요는 없습니다. JDBC 사양에 따라 상위 객체를 닫으면 하위 객체가 자동으로 닫힙니다. 닫기 ConnectionStatement연결이 생성 한 모든 것을 닫 습니다. 어떤 Statement것을 닫으면 그에 ResultSet의해 만들어진 모든 것을 닫을 것 입니다 Statement. Connection풀링 가능 여부 는 중요 하지 않습니다. 풀로 돌아 오기 전에 풀링 가능한 연결도 정리해야합니다.

물론 Connection많은 명령문을 작성 하는 데 긴 중첩 루프가있을 수 있으며 닫는 것이 적절합니다. 나는 거의 결코 가까운 ResultSet하지만, 닫을 때 과도한 것 Statement또는 Connection그것들을 닫습니다.


1

재사용 가능한 One Liner를 만들기 위해 다음 방법을 만들었습니다.

public void oneMethodToCloseThemAll(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
        try {
            if (!resultSet.isClosed()) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            if (!statement.isClosed()) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if (connection != null) {
        try {
            if (!connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

이 코드를 DB 쿼리를 보내는 모든 클래스에 상속 된 부모 클래스에서 사용합니다. resultSet이 없어도 모든 쿼리에서 Oneliner를 사용할 수 있습니다. 메서드는 ResultSet, Statement, Connection을 올바른 순서로 닫는 작업을 처리합니다. 이것이 내 마지막 블록 모양입니다.

finally {
    oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}


-1

일부 편의 기능 :

public static void silentCloseResultSets(Statement st) {
    try {
        while (!(!st.getMoreResults() && (st.getUpdateCount() == -1))) {}
    } catch (SQLException ignore) {}
}
public static void silentCloseResultSets(Statement ...statements) {
    for (Statement st: statements) silentCloseResultSets(st);
}

여기에는 아무것도 닫히지 않습니다. 더 이상 원하지 않더라도 전체 응답을 낭비 적으로 읽는 무의미한 루프입니다.
Lorne의 후작

-1

Java 6 양식을 사용하면 닫히기 전에 닫히지 않았는지 확인하는 것이 좋습니다 (예 : 일부 연결 풀러가 다른 스레드에서 연결을 제거하는 경우)-네트워크 문제와 같은 문 및 결과 집합 상태가 닫힐 수 있습니다. (종종 발생하지는 않지만 Oracle 및 DBCP 에서이 문제가 발생했습니다). 내 패턴은 (이전 Java 구문에서) 다음과 같습니다.

try {
    //...   
    return resp;
} finally {
    if (rs != null && !rs.isClosed()) {
        try {
            rs.close();
        } catch (Exception e2) { 
            log.warn("Cannot close resultset: " + e2.getMessage());
        }
    }
    if (stmt != null && !stmt.isClosed()) {
        try {
            stmt.close();
        } catch (Exception e2) {
            log.warn("Cannot close statement " + e2.getMessage()); 
        }
    }
    if (con != null && !conn.isClosed()) {
        try {
            con.close();
        } catch (Exception e2) {
            log.warn("Cannot close connection: " + e2.getMessage());
        }
    }
}

이론적으로는 닫기 상태 점검과 닫기 자체 사이에 상태 변경의 여지가 거의 없기 때문에 100 % 완벽하지 않습니다. 최악의 경우 오랫동안 경고를 받게됩니다. -그러나 장기 쿼리에서 상태가 변경 될 가능성보다 적습니다. 우리는 생산에서 "평균"하중 (150 명의 동시 사용자)으로이 패턴을 사용하고 있으며 아무런 문제가 없었으므로 경고 메시지를 보지 마십시오.


isClosed()이미 닫혀있는 것을 닫는 것은 아무 문제가 없기 때문에 테스트 가 필요하지 않습니다 . 타이밍 윈도우 문제를 제거합니다. Connection, Statement, 및 ResultSet로컬 변수를 만들어 제거 할 수도 있습니다 .
Lorne의 후작
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.