DAO(Data Access Object)

실제로 DB data에 접근하는 객체
Business Logic이 DB로 부터 데이터를 얻어오기 위해 매번 Driver를 로드하고 Connection 객체를 생성하게 되면 엄청 많은 커넥션이 일어나므로 DAO를 하나 만들어 DB전용 객체로만 쓰는 것

  • Service와 DB를 연결하는 고리의 역할
    • DataBase에 접근하기 위한 로직 & Business Logic 분리위해 사용
  • JPA에서는 DB에 데이터를 CRUD 하는 Repository 객체들이 DAO라고 볼 수 있음


Connection Pool(DBCP = DataBase Connection Pool)

DB와 커넥션을 맺고 있는 객체를 관리하는 역할
(DB 접속할때가 가장 부하가 많이 걸림)

제목 없음

  1. 웹 컨테이너(WAS)가 실행되면서 connection객체(DB와 미리 연결 해놓은 객체)를 pool에 저장해둠
  2. 클라이언트 요청이 오면 connection을 빌려줌
    1) pool에 미리 connection이 생성되어 있기 때문에 요청마다 connection을 생성하는데 드는 연결 시간이 소비되지 않음
    -> DB 접근 시 불필요한 작업(커넥션 생성, 끊기)이 사라지므로 성능향상도 기대
  3. 처리가 끝나면 다시 connection 반납받아 pool에 저장


  • Connection Pool 종류
    • Commons DBCP(아파치에서 제공)
    • Tomcat-JDBC-pool(tomcat에 내장)
    • HikariCP(SpringBoot 2.0부터 default)
  • Connection을 계속해서 재사용하기 때문에 생성되는 Connection 수를 제한적으로 설정
    • 동시 접속자가 많을 경우는?
      • 클라이언트 요청에 대해 pool에서 미리 생성된 connection을 제공하고,
        없을 경우 사용자는 connection이 반환될 때까지 번호순서대로 대기상태로 기다림


  • WAS에서 Connection Pool 크게 설정 -> 메모리 소모 크나 사용자 대기시간 줄어듬
  • Connection Pool 적게 설정 -> 그 만큼 대기 시간이 길어짐


public class MemberDAO {

    private String url = "jdbc:oracle:thin:@localhost:1521:oracle";
    private String uid = "scott";
    private String upw = "tiger";

    public MemberDAO() {  //드라이버 로드
    
        try{
            Class.forName("oracle.jdbc.driver.OracleDriver");
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

    public ArrayList<MemberDTO> memberSelect() {  //유저 목록 조회

        ArrayList<MemberDTO> dtos = new ArrayList<MemberDTO>();

        Connection con =null;
        Statement stmt = null;
        ResultSet rs = null;		

        try{
            con = DriverManager.getConnection(url, uid, upw);  //DB연결
            stmt = con.createStatement();  db실행
            rs = stmt.executeQuery("select * from member");  //쿼리문결과

            while(rs.next()) {
                String name = rs.getString("name");
                String nickname = rs.getString("nickname");
                String pw = rs.getString("pw");
        
                MemberDTO dto = new MemberDTO(name, nickname, pw);
                dtos.add(dto);
            }
     
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(rs != null) rs.close();
                if(stmt != null) stmt.close();
                if(con != null) con.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        return dtos;
    }
}


  • JPA 사용 시
public interface QuestionRepository extends CrudRepository<Question, Long> {
}



DTO(Data Transfer Object)

Layer간 데이터 교환을 하기 위해 사용하는 객체
Client단과 직접 마주하는 계층(Controller 같은)에 Entity를 직접 전달하는 대신 DTO를 사용해 데이터를 교환

제목 없음

  • 로직을 가지지 않는 순수한 데이터 객체(getter & setter 만 가진 클래스)
    • 그저 계층간 데이터 교환이 이루어 질 수 있도록 하는 객체
  • Request와 Response용 DTO는 View를 위한 클래스(Presentation Model)
    • toEntity() 통해 DTO에서 필요한 부분을 이용하여 Entity로 만듦
    • DB에서 data를 얻어 Service나 Controller 등으로 보낼 때 사용하는 객체
      -> Response DTO 형태로 Controller Layer에서 Client에 전달
      • DB의 data가 Presentation Logic Tier로 넘어오게 될 때는 DTO의 모습으로 바뀌어 오고감

=> DTO는 data를 다른 layer간에 주고 받을 때 Domain의 Business Logic을 캡슐화하기 위해 사용된다!


  • ex) User가 입력한 데이터를 DB에 넣는 과정
    1. 유저가 자신의 브라우저에서 데이터를 입력
    2. form에 있는 데이터를 DTO에 넣어서 전송
    3. 해당 DTO를 받은 Server가 DAO를 이용하여 DB로 data를 넣음


@Getter
@Setter  
@NoArgsConstructor
@AllArgsConstructor
public class MemberDTO{
  
	private String name; 
	private String nickname;
	private String pw;		
  
	public Member toEntity() {
	    return new Member(name, nickname, pw);
	}

}


VO(Value Object)와 차이점

값 오브젝트, 값을 위해 쓰임

  • VO의 핵심 역할은 equals()와 hashcode()를 오버라이딩 하는 것
    • VO 내부에 선언된 속성(필드)의 모든 값들이 VO 객체마다 값이 같아야, 똑같은 객체라고 판별
  • DTO와 차이점
    • read-Only 특징(사용하는 도중에 변경 불가능하며 오직 읽기만 가능)
      • ex) 빨강은 Color.RED, 초록은 Color.GREEN 이렇게 단순히 값만 표현하기 위해 getter기능만 존재
    • VO는 특정한 비즈니스 값을 담는 객체, DTO는 Layer간의 통신 용도로 오고가는 객체
      • VO는 리터럴 개념, DTO는 인스턴스 개념
      • VO는 값들에 대해 Read-Only를 보장해줘야 존재의 신뢰성이 확보되지만,
        DTO의 경우는 단지 데이터를 담는 그릇의 역할일 뿐, 값은 그저 전달되어야 할 대상일 뿐



Domain

Domain model(객체)은 내가 개발하고자 하는 영역을 분석하고, 그 분석의 결과로 도출된 model(객체)이라고 할 수 있음
도출한 Domain model은 크게 Entity와 Value로 구분할 수 있음

  • 온라인서점 사이트에서 책을 조회하고 구매한다고 가정
    • 개발자가 구현해야할 소프트웨어의 대상 : 온라인서점
    • 소프트웨어(온라인서점)는 상품 조회, 구매, 결제 등의 기능 제공해야함

=> 이때, 온라인 서점은 소프트웨어로 해결하고자하는 문제 영역, 즉 Domain에 해당

  • 한 도메인은 다시 하위 도메인으로 나눌 수 있음
    • ex) ‘온라인 서점’ 도메인은 다시 주문, 결제, 배송같은 하위 도메인을 가짐


  • 도메인 모델은 특정 도메인을 개념적으로 표현한 것(기본적으로 도메인 자체를 이해하기 위한 개념모델)
    • 객체를 이용한 Domain model
      제목 없음
    • 상태다이어그램을 이용한 Domain model
      제목 없음


Entity

Entity는 Table과 달리 Data Base나 SQL상 실제로 존재하는 것이 아닌, 일종의 개념
Entity는 논리 모델에서 사용, Table은 물리 모델에서 사용

  • 가장 큰 특징은 식별자를 가진다는 점
    = 식별자 외 데이터가 변경된다고 해서 그 객체가 다른 객체가 되는 것이 아님
    • ex) Purchase의 식별자는 주문id, Purchase에서 주문상태를 변경한다고 다른 주문이 되는게 아님
  • Entity class : 실제 DB의 table과 매칭될 클래스
  • Domain Logic만 가지고 있어야 함(Presentation Logic을 가지고 있어서는 안됨)
    여기서 구현한 method는 주로 Service Layer에서 사용
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(nullable = false)
    private String name;
  
    @Column(nullable = false, unique = true)
    private String nickname;  

    @Column(nullable = false)
    private String pw;

    @Builder
    public Member(id, nickname, pw) {
        this.id = id;
        this.name = name;
        this.nickname = nickname;
        this.pw = pw;
    }

    private Member(MemberDTO dto) {
        id = dto.getId();
        name = dto.getName();
        nickname = dto.getNickname();
        pw = dto.getPw();
    }
}


Entity Setter 사용 지양 및 생성자

  • Entity 작성 시 setter를 무분별하게 사용하면?
    -> 의도가 분명하지 않고 객체(Entity)의 값을 언제든지 변경할 수 있음
    => 객체의 일관성, 안정성을 보장할 수 없음
      & 해당 변경이 어디서 누구에 의해 발생했는지 추적하기가 힘듬
  • but 객체의 일관성 유지할 수 있어야
    -> 유지 보수성 올라감

=> setter 사용 최대한 지양
  객체의 생성자에 값들을 넣어줌으로써 setter 사용 줄일 수 있음
  & 값 변경이 필요한 경우 의미 있는 메서드를 생성하여 이를 사용하는 것이 좋음

public Member(int id, String name, String nickname, String pw) {
    this.id = id;
    this.name = name;
    this.id = nickname;
    this.pw = pw;
}


생성자 접근 제한자

  • 기본 생성자 접근 제한자를 protected로 변경하면 new Member() 사용을 제한해 Entity의 일관성을 더 유지할 수 있음
    • 무분별한 객체 생성에 대해 한번 더 체크할 수 있는 수단이 되기 때문(아무런 값도 갖지 않는 의미 없는 객체의 생성을 막게 됨)
    • 기본 생성자 접근 제한자는 protected 까지 허용
      • 기본 생성자의 접근 제한자를 private으로 걸면, 추후에 Lazy Loading 사용 시 Proxy 관련 예외 발생
//@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class Member{

    protected Member(){};
    
}
  • @NoArgsConstructor(access = AccessLevel.PROTECTED) 사용으로도 가능
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class Member{
    
}


Builder Pattern으로 객체 생성 제한

더 나아가 Builder Pattern 사용
-> 해당 Entity 객체를 생성 할때 그 목적과 함께 필요 용도에 따라, 객체 생성을 제한할 수 있음
=> 명시적이고 안전한 Core Data 접근 가능
(Builder 사용 시에도 생성자를 통해 객체를 생성하지만 더욱 명시적으로 객체 생성시에 이유를 알 수 있음)

@Builder
public Member(int id, String name, String nickname, String pw) {
    this.id = id;
    this.name = name;
    this.id = nickname;
    this.pw = pw;
}


Value

식별자가 없음 = 하나의 데이터라도 변경되면 그냥 다른 객체가 됨
=> 같은 객체임을 확실하게 보장하기 위해 Value 타입 불변(immutable)으로 구현하는 것이 좋음
=> 이를 위해 값은 생성자를 통해서만 받고 setter는 아예 구현하지 않음

  • 기존 객체의 데이터를 변경원하면 -> 변경한 데이터로 아예 새로운 객체 만듦
  • Entity 객체는 DB의 테이블과 대응가능, Value는 기본적으로 식별자가 없어 대응될 수 없음


MyBatis의 VO(Value Object) & JPA의 Entity

MyBatis를 쓰는 경우 주로 VO(ValueObject)로 표현
JPA를 쓰는 경우 Entity라고 표현
=> JPA는 ORM이고 MyBatis는 SQL-Mapper이기 때문

  • ORM은 SQL문이 아닌 RDB 객체를 자바 객체로 매핑
    • 객체간 관계, 식별자를 가질 수 있음
  • SQL-Mapper는 SQL문으로 RDB에 접근하고 데이터를 객체로 매핑
    • 객체간 관계나 식별자는 가질 수 없음

==> JPA에서는 식별자를 가지는 Entity,
  MyBatis에서는 값 객체를 의미하는 VO라는 명칭 사용



Reference
https://melonicedlatte.com/2021/07/24/231500.html
https://gmlwjd9405.github.io/2018/12/25/difference-dao-dto-entity.html
https://velog.io/@ohzzi/Entity-DAO-DTO%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C
https://wonyong-jang.github.io/spring/2020/03/17/Spring-Connection-Pool-DBCP.html
https://velog.io/@yeahg_dev/Domain%EA%B3%BC-Entity%EC%9D%98-%EA%B0%9C%EB%85%90
https://doing7.tistory.com/79

업데이트: