-
[스프링부트/웹 애플리케이션 개발]엔티티 설계 주의 사항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'Spring&SpringBoot' 카테고리의 다른 글
[스프링부트/웹 애플리케이션 개발]회원 repository, 회원 service 개발, 인젝션 방법 3가지, @RequiredArgsConstructor (0) 2023.01.04 [스프링부트/웹 애플리케이션 개발]애플리케이션 아키텍처 (3) 2023.01.04 [스프링부트/웹 애플리케이션 개발]엔티티 클래스 개발 -2 (0) 2023.01.04 [스프링부트/웹 애플리케이션 개발]엔티티 클래스 개발 -1 (0) 2023.01.04 [스프링부트/웹 애플리케이션 개발]기본 설계 (0) 2023.01.03