자바(SE)

[java]try~with~resource의 close 호출 시점

  • -

이번 포스트에서는 try~with~resource에서 resource의 close 시점에 대해 살펴보자.

 

try~with~resource

 

resource의 자동 close

try~with~resource 문장은 AutoCloseable 한 resource를 대상으로 사용이 끝나면 자동으로 close()를 호출해주는 기능을 가지고 있다.

// before try~with~resource
@Override
public int insert(Connection con, SsafyMember dto) throws SQLException {
	int result = -1;
	StringBuilder sql = new StringBuilder("sql 작성");
	PreparedStatement pstmt = con.prepareStatement(sql.toString()); // resource
	pstmt.setString(1, dto.getUserId());
    try {
		// business logic 처리
	}finally{
		if(pstmt!=null){ //  자원의 명시적 반납 처리
			pstmt.close();
		}
	}
	return result;
}

// after try~with~resource
@Override
public int insert(Connection con, SsafyMember dto) throws SQLException {
	int result = -1;
	StringBuilder sql = new StringBuilder("sql 작성");
	PreparedStatement pstmt = con.prepareStatement(sql.toString()); // resource
	pstmt.setString(1, dto.getUserId());
    try (pstmt) { // pstmt 자동 close 처리
		// business logic 처리
	}
	return result;
}

 

자동 close()의 호출 시점?

그런데 코딩 하는 과정에 재밋는 현상을 발견했다.

@Override
public int update(SsafyMember member) throws SQLException {
	int result = 0;
	Connection con = DBUtil.getUtil().getConnection();
	try(con) {
		int i = 1/0; // --> 예외 전파됨
		con.commit();
	}catch(Exception e) {
		con.rollback();
		throw e;
	}
	return result;
}

위와 같이 코드를 작성한 상태에서 만약 [문제 발생]부분에서 예외가 발생하면 코드는 어떻게 흘러갈까? 당연히 예외가 catch되서 Transaction이 rollback될 것 같았는데 아래와 같은 오류 메시지를 만나게 되었다.

java.sql.SQLNonTransientConnectionException: No operations allowed after connection closed.
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:111)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:98)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:90)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:64)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:74)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:73)
	at com.mysql.cj.jdbc.ConnectionImpl.rollback(ConnectionImpl.java:1823)
	at com.ssafy.member.model.service.SsafyMemberServiceImpl.insert(SsafyMemberServiceImpl.java:32)
	at com.ssafy.member.test.DaoTest.insertTest(DaoTest.java:33)
	at com.ssafy.member.test.DaoTest.main(DaoTest.java:12)

SsafyMemberService.java의 32라인은 con.rollback()이 실행되는 부분이다. close 한 적이 없는데 어떻게 된 일이지? catch 되기 전에 close()가 이미 진행되었다는 말인가?

 

try~with~resource의 동작

사실 우리가 편하다고 생각하는 자동화 되는 코드들은 내가 써야할 코드들을 컴파일러가 대신 써주는것이 대부분이다. try~with~resource도 마찬가지이다. 우리가 작성한 코드들은 내부적으로 변경된다.( Chapter 14. Blocks and Statements (oracle.com))

try ResourceSpecification
    Block
Catches
Finally
try {
    try ResourceSpecification
        Block     <- 이미 여기서 리소스는 close 됨
}
Catches           <- 이 시점에 resource에 접근하면?
Finally

좌측의 코드는 try~with~resource를 이용해서 작성된 코드다. 이 코드는 실제로 우측의 코드처럼 변경된다. 즉 우리가 만들었던 try~with~resource는 거대한 try 문장으로 감싸지는 것이고 이 내부에서 close()가 이뤄진다.  따라서 Catches에 도달했을 때는 이미 resource는 close된 이후가 된다.

따라서 transaction 처리에서 처럼 resource가지고 commit/rollback등의 작업을 명시적으로 해야하는 경우에는 try~with~resource 문장을 사용하는 것은 적절하지 않다

그렇지 않고 그냥 종료하면 되는 경우(stream을 닫는다든가.., PreparedStatement, ResultSet 등도)는 완전 좋다. ㅎ

 

'자바(SE)' 카테고리의 다른 글

[Thread] 02. Thread의 상태와 제어  (0) 2024.07.04
[Thread] 01. Multi Thread 개념  (0) 2024.07.03
S.O.L.I.D.  (0) 2024.01.09
[JDK] 버전별 특징 - JDK17  (0) 2023.05.23
[JDK] 버전별 특징 - JDK16  (0) 2023.05.23
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.