Java에서 Close 의 의미는?
Java에는 close가 필요한 객체들이 여러개 있다.
Java Application 내에서만 사용되는 자원들이 아니라 운영체제의 자원을 사용하는 것들(File, Network, DB 등)이 이에 해당한다.
꼭 자원의 문제가 아니더라도 종료를 명시하는 close 메서드가 존재한다면 사용 후 호출하기를 의도했을 수 도 있으니 API문서를 잘 확인하여 보자.
여기서는 부스트코스 JDBC 연결 샘플에 있는 내용으로 예를 들어 본다.
Close 방식
-
Java 7 이전의 finally 방식
-
Java 7 try-with-resources 방식
-
Java 9 향상된 try-with-resources 방식
Java 7 이전의 finally 방식
public Role getRole(Integer roleId) {
Role role = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(dburl, dbUser, dbpasswd);
String sql = "SELECT description,role_id FROM role WHERE role_id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, roleId);
rs = ps.executeQuery();
if (rs.next()) {
String description = rs.getString(1);
int id = rs.getInt("role_id");
role = new Role(id, description);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return role;
}
* Class.forName(…) 부분은 java6, jdbc 4.0 버전부터 서비스로더(http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html) 기반으로 JDBC Driver가 자동으로 등록되므로 호출하지 않아도 됩니다만.. 클래스패스에 mysql-connector jar 파일을 넣어도 등록되지 않는 경우가 있어서 생략하지 않고 샘플 그대로 넣어둠.
Java 7 try-with-resource 방식
public Role getRole(Integer roleId) {
Role role = null;
String sql = "SELECT description,role_id FROM role WHERE role_id = ?";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection(dburl, dbuser, dbpasswd);
PreparedStatement ps = conn.prepareStatement(sql)
){
ps.setInt(1, roleId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
String description = rs.getString(1);
int id = rs.getInt("role_id");
role = new Role(id, description);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return role;
}
코드가 확연히 짧아지고 약간 가독성이 향상되긴 했으나 try catch문이 중첩되고 선언되는 resource 영역의 가독성은 떨어진다. 따라서 PreparedStatement에서 인자값을 세팅하는 로직은 AutoCloseable을 구현하는 wrapper class를 만들고 인자값을 전달받아서 한번에 선언하면 가독성이 훨씬 향상될 것이다.
try (
Connection conn = DriverManager.getConnection(dburl, dbuser, dbpasswd);
PreparedStatement ps = PreparedStatementWrapper.prepareStatement(conn, sql, values);
ResultSet rs = ps.executeQuery()
){
.
.
.
}
이런식으로..
Java 9 향상된 try-with-resource 방식
public Role getRole(Integer roleId) {
Role role = null;
String sql = "SELECT description,role_id FROM role WHERE role_id = ?";
Connection conn = DriverManager.getConnection(dburl, dbuser, dbpasswd);
PreparedStatement ps = conn.prepareStatement(sql);
try {
ps.setInt(1, roleId);
} catch (SQLException e) {
e.printStackTrace();
}
ResultSet rs = ps.executeQuery();
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (conn, ps, rs){
if (rs.next()) {
String description = rs.getString(1);
int id = rs.getInt("role_id");
role = new Role(id, description);
}
} catch (Exception e) {
e.printStackTrace();
}
return role;
}
try 블럭 외부에서 생성된 객체를 참조하여 auto close가 가능하기 때문에 try 블럭의 가독성이 훨씬 좋아졌다.