JPA - Entity Mapping

귀찮Lee·2022년 7월 11일
1

Spring

목록 보기
23/30

◎ 엔티티와 테이블 간의 매핑

  • Entity 예시

    @NoArgsConstructor
    @Getter
    @Setter
    @Entity
    public class Member {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long memberId;
    
        @Column(nullable = false, updatable = false, unique = true)
        private String email;
    
        @Column(length = 100, nullable = false)
        private String name;
    
        @Column(length = 13, nullable = false, unique = true)
        private String phone;
    
        @Column(nullable = false)
        private LocalDateTime createdAt = LocalDateTime.now();
    
        @Column(nullable = false, name = "LAST_MODIFIED_AT")
        private LocalDateTime modifiedAt = LocalDateTime.now();
    
        @Transient
        private String age;
    
            public Member(String email) {
            this.email = email;
        }
    
          public Member(String email, String name, String phone) {
            this.email = email;
            this.name = name;
            this.phone = phone;
        }
    }
  • Entity, Id 관련 Annotation

    • @Entity : (Necessary) JPA 관리 대상 Entity가 됨 (DB의 Table과 mapping이 된다.)

      • attribue / name : Entity의 이름을 설정함 (default : 클래스 이름)
    • @Table : (Optional) DB 테이블의 이름 설정 가능

      • attribue / name : DB Table의 이름 설정 (default : 클래스 이름)
    • @Id : (Necessary) 해당 field 값을 DB의 PK(Primary Key) 값으로 지정

    • @GeneratedValue : 기본키 자동 생성 지원 (없을시에는 수동으로 지정해주어야 함)

      • strategy = GenerationType.IDENTITY : 기본키 생성을 DB에 위임(commit 여부와 상관없이 persist때, INSERT 쿼리를 실행하고, PK값을 가져옴)
      • strategy = GenerationType.SEQUENCE : DB에서 제공하는 시퀀스를 사용해서 기본키를 생성
      • strategy = GenerationType.AUTO : JPA가 데이터베이스의 Dialect에 따라서 적절한 전략을 자동으로 선택
      • strategy = GenerationType.TABLE : 별도의 키 생성 테이블을 사용하는 전략 (TABLE을 별도로 만들게되어 추가 쿼리가 발생 -> 성능적으로 좋지 않음)
  • Entity의 필드와 Table의 컬럼과의 매핑 Annotation

    • @Column

      • (Optional) 필드와 컬럼을 매핑
      • 사용하지 않을 경우, 매핑은 되지만, attribute 값이 전부 기본값이 됨
    • @Column attrbute 설정사항

      • nullable : null 값을 허용할 지 여부 (default : true)
        • 원시타입 적용시, nullable = false 로 해놔야 에러를 사전방지 할 수 있다.
      • updatable : 컬럼 데이터를 수정할 수 있는지 여부 (default : false)
      • unique : 중복 제한 조건 추가 (default : false)
      • length : 컬럼에 저장할 수 있는 문자 길이 제한 (default : 255)
      • name : Table Column에 별도의 이름을 지정하여 컬럼을 생성
    • @Transient : 테이블 컬럼과 매핑하지 않음

      • 주로 임시 데이터를 메모리에서 사용하기위한 용도
    • @Enumerated : enum 타입과 매핑할 때 사용하는 애너테이션

      • EnumType.STRING : enum의 이름을 DB에 저장
      • EnumType.ORDINAL : enum의 순서를 나타내는 숫자를 테이블에 저장 (Enum 변경시 이미 저장된 데이터가 일치하지 않게됨, 사용 자제할 것)

◎ Entity와 Table 매핑 사용 권장 방법

  • 특별한 사유 없다면, @Entity와 @Id만 사용
  • 기본키 생성 전략은 IDENTITY 또는 SEQUENCE 전략을 사용
    • DB에서 지원하는 AUTO_INCREMENT 또는 SEQUENCE를 이용
  • @Column 정보 명시
    • 다른 사람이 보았을 때, Entity 클래스를 보고 쉽게 테이블 설계를 알아볼 수 있다.
  • Entity 클래스 필드 타입이 원시타입일 경우, nullable=false 설정을 해야 에러를 사전에 막을 수 있다.
  • @Enumerated 사용시, EnumType.STRING을 사용하자.

◎ Entity 간의 연관 관계 매핑

  • 연관 관계 구현 방법

    • JPA에서는 한 Entity가 다른 Entity를 참조하는 것으로 연관 관계를 구현함
    • DB에서는 Spring Data JDBC에서와 마찬가지로 다른 Table의 PK 값을 FK(Foreign Key)로 가져와서 Column에 저장
  • 연관 관계 종류

    • 단방향 연관 관계 : 한 쪽 클래스에서만 다른 쪽 클래스의 참조 정보를 가지고 있는 관계
    • 양방향 연관 관계 : 양쪽 클래스가 서로의 참조 정보를 가지고 있는 관계를 양방향 연관 관계
    • Spring Data JDBC는 단방향 연관 관계만 지원, JPA는 단방향 양방향 모두 가능
  • 1:N 연관 관계

    • 다대일 단방향 연관관계
      • "다"에 해당하는 Table의 정보를 가져오면, 그것을 참조하는 "일"의 정보도 가져올 수 있다.
      • 가져오는 때는 fetch 라는 값을 통해 설정 가능
    @NoArgsConstructor
    @Getter
    @Setter
    @Entity(name = "ORDERS")
    public class Order {
        ...
    
        @ManyToOne   // 다대일의 관계를 명시
        @JoinColumn(name = "MEMBER_ID")  // 외래키에 해당하는 컬럼명을 적어줌
        private Member member;
        
      public void addOrder(Order order) {
          orders.add(order);
      }
    
    }
    • 다대일 양방향 연관관계
      • 다대일 단방향 연관관계에서, "일" 에서도 "다"에 해당하는 정보 조회가 필요할 때, 사용
      • @JoinColumn(name = "")
        • DB에서 정보가 저장될 쪽에 사용, name 값은 DB에 저장될 Column의 이름이다.
      • attribue / mappedBy
        • 양뱡향 관계에서 주체가 되는 쪽(Many쪽, 외래키가 있는 쪽)을 정의
        • 아래 예시에서 Order("다") 안에서 외래키 역할은 하는 필드를 지정
      // Java mappedBy 오류 내용
      mappedBy reference an unknown target entity property:
      com.codestates.order.Order.ORDERS in com.codestates.member.Member.orders
    @NoArgsConstructor
    @Getter
    @Setter
    @Entity
    public class Member {
        ....
    
        @OneToMany(mappedBy = "member")
        private List<Order> orders = new ArrayList<>();
    
        public void addMember(Member member) {
            this.member = member;
        }
    
    }
    • 다대다 연관관계
      • 중간에 있는 테이블을 만들어, 일대다 연관관계를 양쪽으로 만들어 구현
      • @ManyToMany를 사용하여 구현할 수 있지만,
        • 장점 : 중간 Entity를 따로 구현하지 않아도 된다.
        • 단점 : 둘 사이에 관계에 생기는 field가 필요할 때 만들 수 없다.
          ex) Coffee - Order
    public class Order {
        ...
    
        @OneToMany(mappedBy = "order")
        private List<OrderCoffee> orderCoffees = new ArrayList<>();
    }
    
    public class OrderCoffee {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private long orderCoffeeId;
    
        @ManyToOne
        @JoinColumn(name = "ORDER_ID")
        private Order order;
    
        @ManyToOne
        @JoinColumn(name = "COFFEE_ID")
        private Coffee coffee;
    
        @Column(nullable = false)
        private int quantity;
    }
    
  • 일대일 연관관계

    • @OneToOne 을 이용해서 두 테이블에 연관관계를 지을 수 있다.
    • 정보를 가지고 있을 테이블에서는 @JoinColumn(name = "")을 사용하고, 그렇지 않은 테이블에는 @OneToOne에서 mappedBy 를 사용해서 건너 테이블에서 FK 역할을 하는 field 명시
    @Entity // 일부 어노테이션 생략
    public class Member extends Auditable {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long memberId;
        ...
    
        @OneToOne(mappedBy = "member", cascade = CascadeType.PERSIST)
        private Stamp stamp;
    }
    @Entity // 일부 어노테이션 생략
    public class Stamp extends Auditable {
        ...
    
        @OneToOne
        @JoinColumn(name = "MEMBER_ID")
        private Member member;
    }

◎ Entity Mapping시, 편리한 설정

  • cascade = CascadeType.PERSIST : 영속성 컨텍스트에 한번에 저장

    • 연관관계를 저장하기 위해서는 참조된 객체들이 영속성 컨텍스트에 저장되어 있어야 한다.
    • 위와 같은 설정을 하게 된다면, 참조되지 않은 객체를 참조하고 있다면, 같이 영속성 컨텍스트에 저장한다.
  • cascade = CascadeType.REMOVE : 이 정보 지우면 참조한 정보와 연결 관계를 끊음

  • cascade 참고자료

  • fetch = FetchType.EAGER

    • 쿼리를 통해 정보 조회시, 해당 Entity 와 관련된 정보를 같이 가져온다.
    • 엔티티간의 관계가 복잡해질수록 조인으로 인한 성능 저하가 일어남
  • fetch = FetchType.LAZY

    • 정보가 필요할 때, 관련 값을 가져옴
    • @OneToOne은 LAZY로 설정하더라도, 미리 정보를 가져옴
      (@OneToOne은 기본값으로 설정하여 사용하는 것이 좋다.)
  • fetch 관련 참고자료1, 참고자료2

◎ Entity 간의 연관 관계 매핑 권장 방법

  • 일대다 매핑는 단방향 연관 관계로 사용하지 않음

    • DB로 구현할 때는 "다"의 데이터에 "일"의 FK를 저장함
    • 따라서 저장 시에는 "일(Member)"의 정보를 저장하고, "다(Orders)" 정보에 "일
      (Member)"의 FK을 저장해야함 (쿼리를 두번 사용, 효율이 떨어짐)
  • 우선 적으로 다대일 단방향 매핑을 적용

    • 다대일 단방향 매핑을 통해 그래프 탐색으로 조회 발가능 할 경우, 양방향 매핑을 함
    • 객체 그래프 탐색 : 객체를 통해 다른 객체의 정보를 얻어나가면서 원하는 정보를 찾는 방법

◎ 추가 자료

profile
배운 것은 기록하자! / 오류 지적은 언제나 환영!

0개의 댓글