ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링부트/웹 애플리케이션 개발]엔티티 설계 주의 사항
    Spring&SpringBoot 2023. 1. 4. 16:14

    1) 엔티티에는 가급적 Setter 사용하지 말기

        > Setter가 열려있으면 변경 포인트가 너무 많아서 유지보수가 어렵다.

        > Setter 없이 애플리케이션 충분히 개발할 수 있음

     

     

     

    2) 모든 연관관계는 지연로딩(LAZY)으로 설정하기

        > 즉시로딩(EAGER)은 예측이 어렵고 어떤 SQL이 실행된 지 추적하기 어려움

            > 즉시로딩이란? 예를들어 Member를 조회하는데 연관된 Order도 한 번에 조회 (로딩 시점에 다른 것들도 같이 로딩)

            > 특히 JPQL을 실행할 때 N-1문제가 자주 발생

        > 연관된 엔티티를 함께 DB에서 조회가 필요하다면 fetch join 또는 엔티티 그래프 기능 사용

        > @XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연 로딩으로 설정해야 함

            >> OneToMany 의 경우 기본이 LAZY, 따로 설정하지 않아도 됨

     

     

     

    3) 컬렉션은 필드에서 초기화 하기

    방법 1.

    @Entity
    @Getter @Setter
    public class Member {
    
        @OneToMany(mappedBy = "member")
        private List<Order> orders = new ArrayList<>();
    
    }

    방법 2.

    @Entity
    @Getter @Setter
    public class Member {
    
        @OneToMany(mappedBy = "member")
        private List<Order> orders;
        
        public Member() {
        	orders = new ArrayList();
        }
    
    }

     

    - 컬렉션은 필드에서 바로 초기화하는 것(방법 1.)이 안전하다.

    - 컬렉션은 처음 객체 생성할 때 그대로를 사용하고 가급적 변경하지 않아야 한다.

        > null 문제에서 안전하다.

        > 하이버네이트는 엔티티를 영속화할 때 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다.

           만약 getOrders()처럼 임의의 메서드에서 컬렉션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다. 따라서 필드레벨에서 생성하는 것이 안전하고 코드도 간결하다.

     

     

     

    4) 테이블, 칼럼명 생성 전략

    - 스프링 부트에서 하이버네이트 기본 매핑 전략을 변경해서 실제 테이블 필드명은 다름

     

    하이버네이트 기존 구현: 엔티티의 필드명을 그대로 테이블의 칼럼명으로 사용

    ( SpringPhysicalNamingStrategy )

     

    스프링 부트 신규 설정 (엔티티(필드) 테이블(칼럼))

    1. 카멜 케이스 언더스코어(memberPoint member_point)

    2. .(점) _(언더스코어)

    3. 대문자 소문자

     

    적용 2 단계

    1. 논리명 생성: 명시적으로 칼럼, 테이블명을 직접 적지 않으면 ImplicitNamingStrategy 사용 spring.jpa.hibernate.naming.implicit-strategy : 테이블이나, 칼럼명을 명시하지 않을 때 논리명 적용,

    2. 물리명 적용: spring.jpa.hibernate.naming.physical-strategy : 모든 논리명에 적용됨, 실제 테이블에 적용 (username usernm 등으로 회사 룰로 바꿀 수 있음)

     

     

     

    추가. casecade = CascadeType.ALL

    - 원래는 엔티티당 각각 persist 해줘야 함

    persist(oderItemA);

    persist(oderItemB);

    persist(oderItemC);

    persist(oder);

     

    - casecade = CascadeType.ALL 추가하면 코드 하나면 됨, delete도 마찬가지

    persist(oder);

     

     

     

    추가. 연관관계 (편의) 메서드

    - 원래 비즈니스 코드

    public static void main(String[] args) {
    
    	Member member = new Member();
    	Order order = new Order();
    
    	member.getOrder().add(order);
    	order.setMember(member);
    
    }

    - 양방향 연관관계일 때 연관관계 메서드 생성

    - 만약에 member.getOrder().add(order); 또는 order.setMember(member);

      둘 중 하나의 코드를 놓칠 수 있기 때문에 한 번에 묶는 것

    - 위치는 핵심적으로 컨트롤할 수 있는 곳에 두는 것이 좋다.

     

        public void setMember(Member member) {
            this.member = member;
            member.getOrders().add(this);
        }
        
    public static void main(String[] args) {
    
    	Member member = new Member();
    	Order order = new Order();
    
    	order.setMember(member); //한줄로 코드가 줄어든다
    
    }
        public void addOrderItem(OrderItem orderItem) {
            orderItems.add(orderItem);
            orderItem.setOrder(this);
        }
    
        public void setDelivery(Delivery delivery) {
            this.delivery = delivery;
            delivery.setOrder(this);
        }
    728x90
Designed by Tistory.