DAO vs repository

서은경·2022년 9월 13일
0

Java

목록 보기
17/19

용어정리

  • DAO (Data Access Object)
    데이터베이스에 접근하는 객체
    DB에 접근하도록 DB 접근 관련 로직을 모아둔 객체이다.
  • DTO (Data Transfer Object)
    계층 간 데이터 교환을 위한 자바 Beans
    데이터 베이스 레코드의 데이터를 매핑하기 위한 데이터 객체로 보통 로직을 갖지 않고 getter/setter만 존재함
  • VO (Value Object)
    값 오브젝트로 값을 위해 쓰이며 DTO와 혼용해서 쓰기도 하지만, VO는 불변이며(getter만 존재) DTO는 가변적이라는 차이가 있다. 둘 다 데이터 전송이 목적이라는 공통점을 가지고 있다.
  • repository
    엔티티 객체를 보관하는 저장소의 느낌이며 DAO와 비슷하다고 봐도 무방하다.
  • domain
    실제 DB의 테이블과 매칭시키는 클래스
    DB layer를 위한 클래스로 Entity 클래스이며 최대한 외부에서 getter method를 사용하지 않도록 해당 클래스 안에서 필요한 로직 메소드를 구현한다.

궁금증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 클래스)는 자주 변경되므로 분리해야한다.

0개의 댓글

관련 채용 정보