It's easy, if you try

[SpringBoot] 스프링부트와 JPA 활용 섹션 2 - 도메인 분석 설계 / Entity 클래스 작성 본문

스프링

[SpringBoot] 스프링부트와 JPA 활용 섹션 2 - 도메인 분석 설계 / Entity 클래스 작성

s5he2 2023. 2. 15. 23:05
반응형

도메인 분석 설계

1) 요구사항 분석

실제 동작하는 화면을 보며 기능을 설계한다 

2) 도메인 모델과 테이블 설계

  • RDB는 다대다 관계를 가질 수 없어서 매핑테이블을 가져야함. (1:N / N:1)
  • 공통속성을 가진경우 상속 구조로 표현
  •  

도메인 설계

  • 엔티티 분석 (엔티티가 가지는 속성을 작성)
  •  

엔티티 분석

  • 이때, Address 는 임베디드 타입 - 회원과 배송에서 사용됨.
  • 테이블 분석 (테이블에 맞는 형태로 작성 - PK/ FK 지정, 네이밍 등)
  •  

 

  • 외래 키가 있는 곳을 연관관계의 주인으로 정해라

  • 일대다 관계에서는 다 쪽에 외래키를 지정
  • @ManyToMany 는 사용 지양
    중간 테이블( CATEGORY_ITEM )에 컬럼을 추가할 수 없고, 세밀하게 쿼리를 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있다.
@ManyToMany
@JoinTable(name = "category_item",
	joinColumns = @JoinColumn(name = "category_id"),
	inverseJoinColumns = @JoinColumn(name = "item_id"))
private List<Item> items = new ArrayList<>();

엔티티 클래스 개발

  • 실무에서는 Getter는 열어두고, Setter는 닫아두는 것이 좋다.
  • Setter의 경우 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.
  1. domain 폴더 생성
  2. 엔티티 클래스 생성 (@Entity)
  3. 임베디드 클래스 생성 (@Embadedable , @Embeded)
    임베디드 타입은 사용자가 직접 정의한 값 타입이다. (객체지향적, 응집력 상승)
  4. 추상 클래스 생성 (카테고리에 속함 - 공통속성을 가진경우 상속 구조로 표현)
    자식들은 부모의 속성을 가지면서 자신의 고유 속성을 추가. 
  5. enum 클래스 생성
public enum OrderStatus {
	ORDER, CANCEL
}

@Enumerated(EnumType.STRING) : enum 타입을 숫자가 아닌 지정해둔 string으로 테이블에 저장.

어노테이션

  • @Id : 엔티티 식별자
  • @GeneratedValue: @Id와 같이 붙인다.
  • @Column(name="") : 칼럼명 지정
  • @OneToMany(mappedBy = "member") : 연관관계의 주인이 아닌곳에 지정, member 칼럼에 매핑 
  • @ManyToOne(fetch = FetchType.LAZY):  연관관계의 주인, referencedColumnName 속성을 생략하면 자동으로 일대다 관계를 맺고있는 엔티티의 PK와 매핑이 된다.
  • @JoinColumn(name="") : 테이블에 name으로 매핑해서 칼럼명 저장
  • 추상 클래스 관련 어노테이션
    • @Inheritance(strategy = InheritanceType.SINGLE_TABLE): 상속받은 객체의 칼럼들 모두 하나의 테이블의 속성으로 지정
    • @DiscriminatorColumn(name = "dtype"): 부모 클래스에 선언, 하위 클래스를 구분한기위한 용도이다.
  • @OneToOne(): 1:1 관계에서는 액세스를 더 많이 하는 곳에 FK를 둔다. (FK가 아닌곳에 mappedBy 지정)
    • fetch = FetchType.LAZY 옵션
      • 지연 로딩 (반대는 즉시 로딩 FetchType.EAGER) : 예를 들어 Team이라는 객체에 지연로딩 옵션을 걸어두면 , getTeam을 할때에는 쿼리가 나가지 않고 Team의 프록시 객체가 호출된다. getTeam().getName() 을 하는 경우, 실제로 쿼리가 DB에 조회된다. 만약 관계를 맺고 있는 다른 객체(ex: Member)와 비즈니스 로직상 동시에 사용되는 경우가 더 많다면, 즉시 로딩을 사용하는 것이 좋다.
      • 즉시 로딩 : select를 두번 사용하지 않고, join으로 한번에 조회해온다. 
    • cascade = CascadeType.ALL 옵션
      • @OneToMany나 @ManyToOne 에 옵션으로 줄 수 있는 값
      • Entity의 상태 변화를 전파시키는 옵션
        • Transient
        • Persistent
        • Detached
        • Removed
        • ALL

cascade 옵션이란 @OneToMany 나 @ManyToOne에 옵션으로 줄 수 있는 값이다.

Entity의 상태 변화를 전파시키는 옵션이다.

만약 Entity의 상태 변화가 있으면 연관되어 있는(ex. @OneToMany, @ManyToOne) Entity에도 상태 변화를 전이시키는 옵션이다.

cascade 옵션이란 @OneToMany 나 @ManyToOne에 옵션으로 줄 수 있는 값이다.

Entity의 상태 변화를 전파시키는 옵션이다.

만약 Entity의 상태 변화가 있으면 연관되어 있는(ex. @OneToMany, @ManyToOne) Entity에도 상태 변화를 전이시키는 옵션이다.

엔티티 설계시 주의점

  • 모든 연관관계는 지연로딩으로 설정!
    1. 즉시로딩( EAGER )은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다. 특히 JPQL을 실행할 때 N+1 문제가 자주 발생한다.
    2. 실무에서 모든 연관관계는 지연로딩( LAZY )으로 설정해야 한다.
    3. 연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join 또는 엔티티 그래프 기능을 사용한다.
    4. @XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 한다.
  • 컬렉션은 필드에서 초기화 하자.
    1. 컬렉션은 필드에서 바로 초기화 하는 것이 안전하다. null 문제에서 안전하다.
    2. 하이버네이트는 엔티티를 영속화 할 때, 컬랙션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다. 만약 getOrders() 처럼 임의의 메서드에서 컬력션을 잘못 생성하면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다. 따라서 필드레벨에서 생성하는 것이 가장 안전하고, 코드도 간결하다.
    3. 필드에서 초기화하지 않은 경우는 아래와 같다. 
Member member = new Member();
System.out.println(member.getOrders().getClass());
em.persist(member); // 컬랙션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경
System.out.println(member.getOrders().getClass());

//출력 결과
class java.util.ArrayList
class org.hibernate.collection.internal.PersistentBag
  • 하이버네이트 네이밍 전략
    1. ImplicitNamingStrategy : 사용자에 의해 명시적으로 명명되거나 (@Colume 등) ImplicitNamingStrategy@Table 계약을 통해 Hibernate에 의해 암시적으로 결정된다.
    2. PhysicalNamingStrategy : 현재 스프링부트에서 디폴트로 사용하는 전략이다.
PhysicalNamingStrategy : 
all dots are replaced by underscores and camel casing is replaced by underscores as well. By default, all table names are generated in lower case, but it is possible to override that flag if your schema requires it.

If you prefer to use Hibernate 5’s default instead, set the following property:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

기타

private LocalDateTime orderDate; 

  • java8부터 제공하는 날짜객체

 

참고

- https://ict-nroo.tistory.com/132

- 김영한 님 강의자료

- https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#naming

- https://docs.spring.io/spring-boot/docs/2.1.3.RELEASE/reference/htmlsingle/#howto-configure-hibernate-naming-strategy

반응형
Comments