용어정리
궁금증1
🙋♀️ DAO와 repository 둘다 database에 접근해 데이터를 가져온다는 점에서 똑같은 것 같은데 굳이 왜 용어를 구분해두었을까 ? 차이가 뭘까 ?
💡 구글링에 구글링에 구글링에 구글링을 한 결과, 결론부터 말하자면 Repository는 객체 중심, DAO는 데이터 저장소(DB 테이블) 중심이다. 알맞은 예시인지는 모르겠지만 예시를 보면 좀 더 이해가 잘될까 싶어서 실습한 것중에 dao와 repository 클래스를 가져왔다. 아직 나도 완벽히 이해는 안돼서 좀 더 찾고 공부하고 연구해봐야할 것 같다.
일단 내가 이해한 것은 DAO와 Repository는 모두 DAL(Data Aceess Layer)의 구현체라는 공통점을 가지고 있지만 차이점으로는 DAO가 sql 단계이고 repository가 객체 단계에서 관리가 되고 있다는 점..
Repository
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.stereotype.Repository;
import java.util.*;
//@Repository
public class MemoryMemberRepository implements MemberRepository{
// 실무에서는 다른 거 쓰는데 실습이니까 그냥 ..
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
// 널이어도 반환
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore() {
store.clear();
}
}
DAO
package spring;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
public class MemberDao {
private JdbcTemplate jdbcTemplate;
private MemberRowMapper memberRowMapper = new MemberRowMapper();
public MemberDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Member selectByEmail(String email) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where EMAIL = ?",
/* RowMapper 구현 클래스를 만들어 해당 코드 삭제
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
},
*/
new MemberRowMapper(),
email);
return results.isEmpty() ? null : results.get(0);
}
public void insert(Member member) {
// GeneratedKeyHolder 객체를 생성(자동 생성된 키값을 구해주는 KeyHolder 구현클래스임)
KeyHolder keyHolder = new GeneratedKeyHolder();
//PreparedStatementCreator 객체와 KeyHolder 객체를 파라미터로 가짐
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
// 파라미터로 전달받은 Connection을 이용해서 PreparedStatement 생성
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER(EMAIL, PASSWORD, NAME, REGDATE) values (?,?,?,?)",
new String[]{"ID"}
);
// 인덱스 파라미터의 값 설정
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4, Timestamp.valueOf(member.getRegisterDateTime()));
// 생성한 PreparedStatement 객체 리턴
return pstmt;
}
}, keyHolder);
Number keyValue = keyHolder.getKey();
member.setId(keyValue.longValue());
/* 람다식 사용
jdbcTemplate.update((Connection con) -> {
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE)" + "values (?,?,?,?)",
new String[]{"ID"});
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4, Timestamp.valueOf(member.getRegisterDateTime()));
return pstmt;
}, keyHolder);
*/
}
public void update(Member member) {
jdbcTemplate.update(
"update Member set NAME=?, PASSWORD=? where EMAIL=?",
member.getName(), member.getPassword(), member.getEmail()
);
}
public List<Member> selectAll() {
List<Member> results = jdbcTemplate.query("select * from MEMBER", new MemberRowMapper());
return results;
}
public int count() {
// 두번째 파라미터는 컬럼을 읽어올 때 사용할 타입 지정
Integer count = jdbcTemplate.queryForObject(
"select count(*) from MEMBER", Integer.class
);
return count;
}
public List<Member> selectByRegdate(LocalDateTime from, LocalDateTime to) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where REGDATE between ? and ?" + "order by REGDATE desc",
new MemberRowMapper(), from, to
);
return results;
}
public Member selectById(Long memId) {
List<Member> results = jdbcTemplate.query("select * from MEMBER where ID=?", memberRowMapper, memId);
return results.isEmpty() ? null : results.get(0);
}
}
궁금증2
🙋♀️ Entity 클래스와 DTO 클래스를 분리하는 이유
💡 View Layer와 DB Layer의 역할을 철저하게 분리하기 위해서라고 한다.
테이블과 매핑되는 Entity 클래스가 변경되면 여러 클래스에 영향을 끼치게 되는 반면 View와 통신하는 DTO 클래스(Request / Response 클래스)는 자주 변경되므로 분리해야한다.