[TIL] 2024-08-22

성장일기·2024년 8월 25일

회고

목록 보기
35/37

중요 학습 내용 [JPA]

Entity

  • 테이블 생성

    • autoDDL options

      • create: 기존 테이블을 밀고 새로 만든다
      • create-drop: 서버 실행시 생성. 이후 서버 종료시 drop
      • update: 변경사항에 대해서 반영
    • Entity

      @Entity(name = "member")      // 엔티티 객체로 만들기 위한 어노테이션, 다른 패키지에 동일한 이름 존재 불가
      @Table(name="tbl_member")     //  데이터베이스에 매핑될 테이블 이름 설정
      public class Member {
      
      @Id
      @Column(name="member_no")           //  데이터베이스에 대응되는 컬럼명 지정(타입 및 여러 제약조건 설정 가능)
      private int memberNo;
      
      @Column(name="member_id")
      private String memberId;
      ...
      
      @Column(name="status")
      private String status;
      

Field

...
    
@Column(name="member_no") // 데이터베이스에 대응되는 컬럼명 지정(타입 및 여러 제약조건 설정 가능)
private int memberNo;
...
@Column(name="phone", columnDefinition = "varchar(200)", unique = true, nullable = false)
private String phone;
    
@Column(name="enroll_date")
@Temporal(TemporalType.TIMESTAMP)       // datetime
@Temporal(TemporalType.DATE)            // date
@Temporal(TemporalType.TIME)            // time
private Date enrollDate;
  • pk

    • id(identity)

    • table

      • number generate table: 번호 발생 및 발생 번호 관리 테이블
        • sequence cache:
          • 먼저 번호를 뽑아놓아 관리한다.
          • 캐쉬 값을 낮추면 성능이 저하되기에, 필요하다.
    • Sequence 객체를 활용(only for Oracle)

    • 복합키

      • Embeddable Type: 하나의 값의 묶음이자 불변 객체로 다루는 타입

      • 불변 객체

      • equals, hashCode를 반드시 오버라이딩 해야 함

        • 두 객체가 하나의 값으로 PK 역할을 하기에 하나의 필드만 수정 불가
        • 따라서 동등 비교가 가능해야함
        • 내부적으로 Serializable을 구현
        • VO와 유사함
        • @EmbeddedId로 복합키를 표현하고자 할 때 쓰일 수 있다.
      • [Member.java]

        @Entity(name = "member_section05_subsection01")
        @Table(name="tbl_member_section05_subsection01")
        public class Member {
            @EmbeddedId
            private MemberPK memberPK;
            ...
        }
           
      • [MemberPK.java]

        @Embeddable
        public class MemberPK implements Serializable {
            @Column(name="member_no")
            private int memberNo;
        
            @Column(name="member_id")
            private String memberId;
        
            public MemberPK() {
            }
        
            public MemberPK(int memberNo, String memberId) {
                this.memberNo = memberNo;
                this.memberId = memberId;
            }
        
            @Override
            public boolean equals(Object object) {
                if (this == object) return true;
                if (object == null || getClass() != object.getClass())
                return false;
                MemberPK memberPK = (MemberPK) object;
                return memberNo == memberPK.memberNo && Objects.equals(memberId, memberPK.memberId);
            }
        
            @Override
            public int hashCode() {
                return Objects.hash(memberNo, memberId);
            }
        
            @Override
            public String toString() {
                return "MemberPK{" +
                        "memberNo=" + memberNo +
                        ", memberId='" + memberId + '\'' +
                        '}';
            }
        }
      • idclass

      • [Member.java]

        @IdClass(MemberPK.class)
        public class Member {
        @Id
        @Column(name="member_no")
        private int memberNo;
        @Id
        @Column(name="member_id")
        private String memberId;            
        ...            
        }
      • [MemberPK.java]

        public class MemberPK implements Serializable {
            private int memberNo;
            private String memberId;
        
            public MemberPK() {
            }
        
            public MemberPK(int memberNo, String memberId) {
                this.memberNo = memberNo;
                this.memberId = memberId;
            }
        
            @Override
            public boolean equals(Object object) {
                if (this == object) return true;
                if (object == null || getClass() != object.getClass()) return false;
                MemberPK memberPK = (MemberPK) object;
                return memberNo == memberPK.memberNo && Objects.equals(memberId, memberPK.memberId);
            }
        
            @Override
            public int hashCode() {
                return Objects.hash(memberNo, memberId);
            }
        }
  • Enum

    • use

    • [Member.java]

      ...
      @Column(name="member_role")
      @Enumerated(EnumType.ORDINAL)       // DB에 숫자로 값이 들어감(ex: 0 또는 1)
      @Enumerated(EnumType.STRING)        // DB에 문자열로 값이 들어감(ex: ADMIN 또는 MEMBER)
      private RoleType memberRole;
      ...
    • [RoleType.java]

      public enum RoleType {
          ROLE_ADMIN, 
          ROLE_MEMBER    // SpringSecurity에 적용
      }
  • access level

    • 클래스 레벨과 필드 레벨, 프로퍼티 레벨에서 접근방식 설정 가능

Mapping

Unidirectional Relation

  • Association(ManyToOne)

@Entity(name="menu_and_category")
@Table(name="tbl_menu")
public class MenuAndCategory {

    @Id
    @Column(name="menu_code")
    private int menuCode;

    @Column(name="menu_name")
    private String menuName;

    @Column(name="menu_price")
    private int menuPrice;

    // JoinColumn에 쓰이는 컬럼명은 FK 제약조건이 걸린 자식 테이블의 컬럼명 작성
    @JoinColumn(name="category_code")
    @ManyToOne
    private Category category;

    @Column(name="orderable_status")
    private String orderableStatus;
  • Collection(OneToMany)

    • Anti-Pattern에 가까움

    • N+1 문제 발생

      • use

        @Entity
        public class CategoryAndMenu {
            @Id
            @Column(name="category_code")
            private int categoryCode;
        
            ...
        
            // JoinColumn에 쓰이는 컬럼명은 FK 제약조건이 걸린 자식 테이블의 컬럼명
            @JoinColumn(name="category_code")
            @OneToMany                        // 두 엔티티 간의 전체 카디널리티(N:1)를 고려하여 작성
            private List<Menu> menu;          //  하나의 메뉴는 하나의 카테고리를 지님
        }
        • N+1 문제: 부모 엔티티가 관련된 모든 자식 엔티티를 가져옴
          • 따라서 하나의 엔티티 조회 시, 하위의 여러 엔티티 갯수만큼 쿼리 발생
        • 사실, JPA 패러다임은 엔티티 매니저에게 모든 작업을 맡기기에, 로그를 파헤치는 식의 접근은 JPA로서 안티패턴일 수 있다.
        • Lazy Join / Fetch Join으로 해결 가능하나 여러 단점들이 있다.

Bidirectional Relation

  • 객체 그래프 탐색을 통한 조회

  • 객체 그래프(객체간의 연관관계를 표현) 탐색:

    • 객체 그래프의 연관관계를 활용하여 탐색
  • [Menu.java]
@Entity
@Table(name="tbl_menu")
public class Menu {

    @Id
    @Column(name="menu_code")
    private int menuCode;
    
    ...
    
    @JoinColumn(name="category_code")
    @ManyToOne
    private Category category;
    
    ...

    public Category getCategory() {
        return category;
    }

    @Override
    public String toString() {
        return "Menu{" +
                "menuCode=" + menuCode +
                ", menuName='" + menuName + '\'' +
                ", menuPrice=" + menuPrice +
                ", category=" + category +		// HotSpot
                ", orderableStatus='" + orderableStatus + '\'' +
                '}';
    }
}
  • [Category.java]
@Entity
@Table(name="tbl_category")
public class Category {
    @Id
    @Column(name="category_code")
    private int categoryCode;

    @OneToMany(mappedBy="category")         // 연관관계의 주인: mappedBy
    private List<Menu> menuList;
    
    ...

    public List<Menu> getMenuList() {
        return menuList;
    }

    @Override
    public String toString() {
        return "Category{" +
                "categoryCode=" + categoryCode +
                ", categoryName='" + categoryName + '\'' +
                ", refCategoryCode=" + refCategoryCode +
                ", menuList=" + menuList +		// HotSpot
                '}';
    }
}
  • 순환참조 발생

    • log: java.lang.StackOverflowError
  • 양방향 연관관계는 지양되는 방식이며 순환참조 주의(toString())

  • 연관관계의 주인인 엔티티는 한번에 join문으로 관계를 맺은 엔티티를 조회

부모-자식 관계에서 자식 테이블이 연관관계의 주인

  • 따라서 자식이 관계를 끊으면, 연관관계가 사라짐
profile
엔지니어로의 성장일지

0개의 댓글