JDBC연동을 위해 알아야 할 내용들
add() & findById()
Factory, Spring 적용 후 코드
public void add(User user) {
Map<String, String> env = System.getenv();
try {
// DB접속 (ex sql workbeanch실행)
Connection c = cm.makeConnection();
// Query문 작성
PreparedStatement pstmt = c.prepareStatement("INSERT INTO users(id, name, password) VALUES(?,?,?);");
// 매개변수로 받아온 user 객체를 PreparedStatement에
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getPassword());
// Query문 실행
pstmt.executeUpdate();
pstmt.close();
c.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public User findById(String id) {
Map<String, String> env = System.getenv();
Connection c;
try {
// DB접속 (ex sql workbeanch실행)
c = cm.makeConnection();
// Query문 작성
PreparedStatement pstmt = c.prepareStatement("SELECT * FROM users WHERE id = ?");
pstmt.setString(1, id);
// Query문 실행
ResultSet rs = pstmt.executeQuery();
rs.next();
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
rs.close();
pstmt.close();
c.close();
return user;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
Java
복사
코드 이해하기
위의 코드에서 발생할 수 있는 문제점은?
만약, Connection 연결은 됐는데, Query문을 잘못 작성하여 PreparedStatement가 생성되지 않았다면 SQLException으로 인해 catch문으로 넘어감
Connection과 PreparedStatement의 close() 즉, 자원의 반환이 이루어지지 않아 Connection Pool에 반환되지 않은 자원들이 계속 쌓이게 됨
Refactoring (finally)
public void add(User user) {
Map<String, String> env = System.getenv();
try {
// DB접속 (ex sql workbeanch실행)
Connection c = cm.makeConnection();
// Query문 작성
PreparedStatement pstmt = c.prepareStatement("INSERT INTO users(id, name, password) VALUES(?,?,?);");
// 매개변수로 받아온 user 객체를 PreparedStatement에
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getPassword());
// Query문 실행
pstmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally { // ✨ 확인할 곳
pstmt.close();
c.close();
}
}
public User findById(String id) {
Map<String, String> env = System.getenv();
Connection c;
try {
// DB접속 (ex sql workbeanch실행)
c = cm.makeConnection();
// Query문 작성
PreparedStatement pstmt = c.prepareStatement("SELECT * FROM users WHERE id = ?");
pstmt.setString(1, id);
// Query문 실행
ResultSet rs = pstmt.executeQuery();
rs.next();
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
return user;
} catch (SQLException e) {
throw new RuntimeException(e);
}finally { // ✨ 확인할 곳
rs.close();
pstmt.close();
c.close();
}
}
Java
복사
UserDao.java
리팩토링한 코드에서 발생할 수 있는 문제
만약 Connection 연결이 되었고 PreparedStatement에서 문제가 생긴 경우를 생각해보자. Connection 객체 생성은 되었지만 PreparedStatement의 객체는 생성되지 않았다. 그럼 아무리 finally에 close() 를 작성하였다고 해도 반환하고 싶어도 반환할 자원이 없다.
Refactoring2 (조건문, try~catch)
public void add(User user) {
Map<String, String> env = System.getenv();
Connection c = null;
PreparedStatement pstmt = null;
try {
// DB접속 (ex sql workbeanch실행)
c = cm.makeConnection();
// Query문 작성
pstmt = c.prepareStatement("INSERT INTO users(id, name, password) VALUES(?,?,?);");
// 매개변수로 받아온 user 객체를 PreparedStatement에
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getPassword());
// Query문 실행
pstmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally { // ✨ 확인할 곳
if(pstmt != null) {
try {
pstmt.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
if(c != null) {
try {
c.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
}
}
public User findById(String id) {
Map<String, String> env = System.getenv();
Connection c = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// DB접속 (ex sql workbeanch실행)
c = cm.makeConnection();
// Query문 작성
pstmt = c.prepareStatement("SELECT * FROM users WHERE id = ?");
pstmt.setString(1, id);
// Query문 실행
rs = pstmt.executeQuery();
rs.next();
User user = new User(rs.getString("id"), rs.getString("name"),
rs.getString("password"));
return user;
} catch (SQLException e) {
throw new RuntimeException(e);
}finally { // ✨ 확인할 곳
if(rs != null) {
try {
rs.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
if(pstmt!= null) {
try {
pstmt.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
if(c != null) {
try {
c.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
}
}
Java
복사
UserDao.java
deleteAll()
public void deleteAll() throws SQLException {
Connection c = makeConnection();
PreparedStatement ps= c.prepareStatement("DELETE FROM users");
ps.executeUpdate();
ps.close();
c.close();
}
Java
복사
•
statement에 delete문을 저장하고 업데이트 합니다.
•
DELETE vs TRUNCATE
◦
DELETE : 조건이 없으면 TRUNCATE와 동일하게 모든 데이터가 삭제되고 테이블 스키마만 남는 형태가 됩니다.
◦
차이점 : DELETE명령어는 데이터 복구가 가능하고, 로그가 남습니다.
•
Connection과 Statement를 생성할 때 오류가 발생했다면?
⇒ close() 하는 코드가 실행되지 않고 중간에 멈춰버립니다.
로컬 환경에선 문제가 없지만 서버에서 실행된다면 서버가 다운될 수도 있습니다.
public void deleteAll() throws SQLException {
Connection c = null;
PreparedStatement ps = null;
try {
c = makeConnection();
ps = c.prepareStatement("DELETE FROM users");
ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (ps != null) {
try{
ps.close();
}catch (SQLException e){
}
}
if (c != null) {
try{
c.close();
}catch (SQLException e){
}
}
}
}
Java
복사
다음과 같이 try-catch 문으로 감싸주면 Connection과 Statement를 생성할 때 문제가 발생해도 close()문이 실행됩니다.
getCount()
public int getCount1() throws SQLException {
Connection conn = makeConnection();
PreparedStatement ps = conn.prepareStatement("SELECT COUNT(ID) FROM users");
ResultSet rs = ps.executeQuery();
rs.next();
int s = Integer.parseInt(rs.getString("COUNT(ID)"));
conn.close();
ps.close();
rs.close();
return s;
}
Java
복사
•
getCount() 는 모든 레코드의 갯수를 세어서 반환해주는 메소드입니다.
•
DB에서 데이터를 가져오기 때문에 ResultSet을 사용해주어야 합니다.
public int getCount() throws SQLException {
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
int s;
try {
conn = makeConnection();
ps = conn.prepareStatement("SELECT COUNT(ID) FROM users");
rs = ps.executeQuery();
rs.next();
s = Integer.parseInt(rs.getString("COUNT(ID)"));
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
}
return s;
}
Java
복사
다음과 같은 방법으로 개선할 수 있습니다.
public int getCount() throws SQLException {
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
int s;
try {
conn = makeConnection();
ps = conn.prepareStatement("SELECT COUNT(ID) FROM users");
rs = ps.executeQuery();
rs.next();
s = Integer.parseInt(rs.getString("COUNT(ID)"));
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
close(conn, ps, rs);
}
return s;
}
Java
복사
•
close() 함수는 getCount 말고도 다른 모든 메소드에서 사용할 수 있습니다. 보다 간결하게 코딩을 할 수 있습니다. close()를 구현해보겠습니다.
private void close(AutoCloseable... autoCloseables){
for (AutoCloseable ac : autoCloseables) {
if (ac != null) {
try {
ac.close();
} catch (Exception e) {
throw new RuntimeException(e)
}
}
}
}
Java
복사
파라미터에 있는 … 은 매개변수로 얼만큼 오든 알아서 받아줍니다.
Connection, ResultSet, Statement 모두 같은 인터페이스를 상속 받기 때문에 다음과 같이 AutoCloseable로 넘겨줄 수 있습니다.