1. DAO(Data Access Object)란?
- 데이터(data)에 접근(access)하기 위한 객체(object)
- Database에 저장된 데이터를 읽기, 쓰기, 삭제, 변경을 수행
- DB테이블당 하나의 DAO를 작성
2. 계층(layer)의 분리
- 컨트롤러가 직접 데이터베이스에 접근하면 메서드의 중복이 일어난다.
- 중간에 UserDao를 통해 간접적으로 DB에 접근
- 분리 - 관심사, 변하는것과 변하지 않는것의 분리, 중복
- UserDao 클래스
public class UserDao {
@Autowired
DataSource ds;
final int FAIL = 0;
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()를 호출하다가 예외가 발생할 수 있으므로, try-catch로 감싸야함.
// try { if(pstmt!=null) pstmt.close(); } catch (SQLException e) { e.printStackTrace();}
// try { if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace();}
close(pstmt, conn); // private void close(AutoCloseable... acs) {
}
}
public User selectUser(String id) throws Exception {
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테이블에 저장하는 메서드
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하는 메서드
public int updateUser(User user) {
int rowCnt = FAIL; // insert, delete, update
// Connection conn = null;
// PreparedStatement pstmt = null;
String sql = "update user_info " +
"set pwd = ?, name=?, email=?, birth =?, sns=?, reg_date=? " +
"where id = ? ";
// try-with-resources - since jdk7
try (
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(); }
}
}
- 사용하고 나면 Connection, PreparedStatement, ResultSet과 같은 메모리를 차지하는 변수들을 close 해줘야하는데사용한 순서의 역순으로 close를 해줘야 한다.
- try-width-resources : 예외가 발생하건 안하건 자동으로 close된다. - java7이상, AutoCloseable을 구현한 객체만
try (
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;
}
- sql문에 넣어주기 위해 updateUser메서드에서 Date를 java.sql.Date로 형변환
- 시간과 날짜를 둘다 넣어주기위해 Timestamp를 사용
- UserDao를 인터페이스로 만들어보자
- class UserDao에서 오른쪽 마우스 클릭 Refactor -> Extract Interface 클릭
- 원래 UserDao클래스가 UserDaoImpl로 바뀌고 UserDao가 인터페이스가 되어 생성되었다.
- 인터페이스의 모든 메서드는 public abstract가 생략 가능
- Test
- Test자동생성
class UserDaoImplTest{
@Test
void deleteUser() {
}
@Test
void selectUser() {
}
@Test
void insertUser() {
}
@Test
void updateUser() {
}
}
- Test양식이 만들어졌다. 여기에 UserDao를 구현한 UserDaoImpl을 사용하는게 아니라 UserDao를 통해 간접적으로 사용해준다.
- @Autowired가 안된다고 나오는데 @Repository (@Conponent가 메타에너테이션으로 들어가있다)를 추가
- 에러가 사라졋다.
- updateUser부터 Test를 해보면...
@Test
void updateUser() {
User user = new User("asdf", "1234", "abc", "aaa@aaa.com", new Date(), "fb", new Date());
int rowCnt = UserDao.insertUser(user);
assertTrue(rowCnt == 1);
}
- 에러가 발생하는데...
- 첫번째, root-context.xml의 경로를 WEB-INF/spring/root-context.xml로 만들어준다.
- 두번째, @ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})
경로에 하위경로추가 - 세번째, pom.xml을 고쳐줬다.
- 네번째, 모든 Test는 public으로 돌아간다.
- 이렇게 해주니....
- 에러가 사라졌다.
@Test
public void updateUser() throws Exception{
userDao.deleteUser("asdf");
User user = new User("asdf", "1234", "abc", "aaa@aaa.com", new Date(), "fb", new Date());
int rowCnt = userDao.insertUser(user);
assertTrue(rowCnt == 1);
user.setPwd("4321");
user.setEmail("bbb@bbb.com");
rowCnt = userDao.updateUser(user);
assertTrue(rowCnt==1);
User user2 = userDao.selectUser(user.getId());
System.out.println("user = " + user);
System.out.println("user2 = " + user2);
assertTrue(user.equals(user2));
}
- updateUser메서드를 조금 더 추가해주면...
- 마지막에 user와 user2가 달라서 에러가 나오지만 나머지는 잘 된 모습이다.
- user2의 Birth의 시간이 안나오는 이유는 SQL의 Date 타입은 날짜만 들어가기때문에 자바의 Util의 Date의 시간정보가 짤린것.
@Test
public void updateUser() throws Exception{
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2025,1,26);
userDao.deleteUser("asdf");
User user = new User("asdf", "1234", "abc", "aaa@aaa.com", new Date(cal.getTimeInMillis()), "fb", new Date());
int rowCnt = userDao.insertUser(user);
assertTrue(rowCnt == 1);
user.setPwd("4321");
user.setEmail("bbb@bbb.com");
rowCnt = userDao.updateUser(user);
assertTrue(rowCnt==1);
User user2 = userDao.selectUser(user.getId());
System.out.println("user = " + user);
System.out.println("user2 = " + user2);
assertTrue(user.equals(user2));
}
- Calendar를 사용해서 Date를 만들어준다.
- 다 같은데 실패가 나온다
'Spring DI, AOP' 카테고리의 다른 글
13. Spring으로 DB다루기/TDD - 패스트캠퍼스 백엔드 부트캠프 3기 (0) | 2025.02.26 |
---|---|
12. Spring으로 DB연결하기 - 패스트캠퍼스 백엔드 부트캠프 3기 (0) | 2025.02.26 |
11. 데이터 모델링이란 - 패스트캠퍼스 백엔드 부트캠프 3기 (0) | 2025.02.25 |
10. AOP의 원리와 용어 - 패스트캠퍼스 백엔드 부트캠프 3기 (0) | 2025.02.24 |
9. 외부 설정 사용하기 - 패스트캠퍼스 백엔드 부트캠프 3기 (2) | 2025.02.24 |