✔️ 데이터(data)에 접근(access) 하기 위한 객체(object)
Database에 저장된 데이터를 읽기, 쓰기, 삭제, 변경을 수행
➡️ CRUD
DB테이블당 하나의 DAO를 작성
: 하나의 DAO가 하나의 DB테이블에 대한 CRUD를 한다.
Controller에서 DAO를 이용해서 데이터베이스를 다루게 된다.
❗️ Controller에서 직접 db에 접근할 수도 있다.
➡️ db에 관한 메서드들이 중복이 발생
✔️ 관심사 + 중복 코드의 분리
각 Controller들이 가지고 있던 db메서드들을 별도의 객체(UserDao
)로 만들어서 분리하고 있다.
: userDao
를 가지고 간접적으로 데이터베이스에 접근할 수 있게 바뀌었다.
➡️ 변경 유리
LoginController & RegisterController
: Presentation Layer Data를 보여주는 계층
userDao
: 영속 계층(Data Access Layer | Persistence Layer)
: 다른 SQL로 바꾸기에도 유리하다.
원래는 Business Layer라고 중간에 또 분리를 한다. (지금은 간단하게!)
✏️ UserDao를 만들어보고 테스트하며, Controller들이 주입받으며 사용하게 만들어보자
package kr.ac.jipark09;
public interface UserDao {
int deleteUser(String id);
User selectUser(String id);
// 사용자 정보를 user_info테이블에 저장하는 메서드
int insertUser(User user);
// 매개변수로 받은 사용자 정보로 user_info테이블을 update하는 메서드
int updateUser(User user);
void deleteAll() throws Exception;
}
package kr.ac.jipark09;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
//@Component - @Controller, @Repository, @Service, @ControllerAdvice
@Repository // @Component의 메타 어노테이션 그래서 자동 스캔 가능
public class UserDaoImpl implements UserDao {
@Autowired
DataSource ds;
final int FAIL = 0;
@Override
public int deleteUser(String id) {
int rowCnt = FAIL; // insert, delete, update
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "delete from user_info where id= ? ";
try {
conn = ds.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
// int rowCnt = pstmt.executeUpdate(); // insert, delete, update
// return rowCnt;
return pstmt.executeUpdate(); // insert, delete, update
} catch (SQLException e) {
e.printStackTrace();
return FAIL;
} finally {
close(pstmt, conn); // private void close(AutoCloseable... acs) {
}
}
@Override
public User selectUser(String id) {
User user = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select * from user_info where id= ? ";
try {
conn = ds.getConnection();
pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
pstmt.setString(1, id);
rs = pstmt.executeQuery(); // select
if (rs.next()) {
user = new User();
user.setId(rs.getString(1));
user.setPwd(rs.getString(2));
user.setName(rs.getString(3));
user.setEmail(rs.getString(4));
user.setBirth(new Date(rs.getDate(5).getTime()));
user.setSns(rs.getString(6));
user.setReg_date(new Date(rs.getTimestamp(7).getTime()));
}
} catch (SQLException e) {
return null;
} finally {
// close()를 호출하다가 예외가 발생할 수 있으므로, try-catch로 감싸야함.
// close()의 호출순서는 생성된 순서의 역순
// try { if(rs!=null) rs.close(); } catch (SQLException e) { e.printStackTrace();}
// try { if(pstmt!=null) pstmt.close(); } catch (SQLException e) { e.printStackTrace();}
// try { if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace();}
close(rs, pstmt, conn); // private void close(AutoCloseable... acs) {
}
return user;
}
// 사용자 정보를 user_info테이블에 저장하는 메서드
@Override
public int insertUser(User user) {
int rowCnt = FAIL;
Connection conn = null;
PreparedStatement pstmt = null;
// insert into user_info (id, pwd, name, email, birth, sns, reg_date)
// values ('asdf22', '1234', 'smith', 'aaa@aaa.com', '2022-01-01', 'facebook', now());
String sql = "insert into user_info values (?, ?, ?, ?,?,?, now()) ";
try {
conn = ds.getConnection();
pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getPwd());
pstmt.setString(3, user.getName());
pstmt.setString(4, user.getEmail());
pstmt.setDate(5, new java.sql.Date(user.getBirth().getTime()));
pstmt.setString(6, user.getSns());
return pstmt.executeUpdate(); // insert, delete, update;
} catch (SQLException e) {
e.printStackTrace();
return FAIL;
} finally {
close(pstmt, conn); // private void close(AutoCloseable... acs) {
}
}
// 매개변수로 받은 사용자 정보로 user_info테이블을 update하는 메서드
@Override
public int updateUser(User user) {
int rowCnt = FAIL; // insert, delete, update
String sql = "update user_info " +
"set pwd = ?, name=?, email=?, birth =?, sns=?, reg_date=? " +
"where id = ? ";
// try-with-resources - since jdk7
// AutoCloseable만 가능 -> Connection / PerparedStatement 둘다 인터페이스지만 구현하는 클래스가 AutoCloseable이다.
try (
// 자동으로 close해줌
Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
) {
pstmt.setString(1, user.getPwd());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getEmail());
pstmt.setDate(4, new java.sql.Date(user.getBirth().getTime()));
pstmt.setString(5, user.getSns());
pstmt.setTimestamp(6, new java.sql.Timestamp(user.getReg_date().getTime()));
pstmt.setString(7, user.getId());
rowCnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
return FAIL;
}
return rowCnt;
}
public void deleteAll() throws Exception {
Connection conn = ds.getConnection();
String sql = "delete from user_info ";
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
pstmt.executeUpdate(); // insert, delete, update
}
private void close(AutoCloseable... acs) {
for(AutoCloseable ac :acs)
try {
if(ac != null) {
ac.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
Reference
: https://fastcampus.co.kr/dev_academy_nks