예제에서는 설명을 쉽게하기 위해 엔티티 클래스에 Getter, Setter를 모두 열고, 최대한 단순하게 설계

실무에서는 가급적 Getter는 열어두고, Setter는 꼭 필요한 경우에만 사용하는 것을 추천

 

> 참고: 이론적으로 Getter, Setter 모두 제공하지 않고, 꼭 필요한 별도의 메서드를 제공하는게 가장 이상적 이다.

하지만 실무에서 엔티티의 데이터는 조회할 일이 너무 많으므로, Getter의 경우 모두 열어두는 것이 편리하다.

Getter는 아무리 호출해도 호출 하는 것 만으로 어떤 일이 발생하지는 않는다.

하지만 Setter는 문제가 다르다. Setter를 호출하면 데이터가 변한다. 

Setter를 막 열어두면 가까운 미래에 엔티티에가 도대체 왜 변경되는지 추적하기 점점 힘들어진다. 그래서 엔티티를 변경할 때는 Setter 대신에 변경 지점이 명확 하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.

 

 

회원 엔티티

@Entity
@Getter @Setter
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    
    private String name;
    
    @Embedded
    private Address address;
    
    @OneToMany(mappedBy = "member") // 연관관계의 주인이 아니다 - 맵핑하는게 아니라 맵핑되는 거울이다 - 읽기 전용이다
    private List<Order> orders = new ArrayList<>();
}

참고: 엔티티의 식별자는 id 를 사용하고 PK 컬럼명은 member_id 를 사용했다. 엔티티는 타입(여기서는 Member )이 있으므로 id 필드만으로 쉽게 구분할 수 있다. 테이블은 타입이 없으므로 구분이 어렵다.

그리고 테이블은 관례상 테이블명 + id 를 많이 사용한다. 참고로 객체에서 id 대신에 memberId 를 사용해도 된다. 

중요한 것은 일관성이다.

 

 

주문 엔티티

@Entity
@Table(name = "orders") // 테이블명 설정
@Getter @Setter
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id") // FK //연관관계 주인
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) // 하나의 배송정보는 하나의 주문정보를 가진다
    @JoinColumn(name = "delivery_id") // FK // 연관관계 주인
    private Delivery delivery;

    private LocalDateTime orderDate; // 주문시간

    @Enumerated(EnumType.STRING) // EnumType.ORDINAL은 숫자로 들어감 사용하지 말 것.
    private OrderStatus status; // 주문상태 [ORDER, CANCEL]

}

 

나머지 클레스 생략

 

 

참고: 실무에서는 @ManyToMany 를 사용하지 말자

> @ManyToMany 는 편리한 것 같지만, 중간 테이블( CATEGORY_ITEM )에 컬럼을 추가할 수 없고, 세밀하게 쿼리를 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있다. 중간 엔티티( CategoryItem 를 만들고 @ManyToOne , @OneToMany 로 매핑해서 사용하자.

정리하면 대다대 매핑을 일대다, 다대일 매핑으로 풀어내서 사용하자.

 

 

주소 값 타입

@Embeddable
@Getter
public class Address {
    private String city;
    private String street;
    private String zipcode;
    
    protected Address() {
    }
    
    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }
}

참고: 값 타입은 변경 불가능하게 설계해야 한다.

> @Setter 를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들자. JPA 스펙상 엔티티나 임베디드 타입( @Embeddable )은 자바 기본 생성자(default constructor)를 public 또는 protected 로 설정해야 한다.

public 으로 두는 것 보다는 protected 로 설정하는 것이 그나마 더 안전하다.

> JPA가 이런 제약을 두는 이유는 JPA 구현 라이브러리가 객체를 생성할 때 리플랙션 같은 기술을 사용할 수 있도록 지원해야 하기 때문이다.

'프로그래밍 > SpringBoot & JPA Use1' 카테고리의 다른 글

casecade  (0) 2022.06.19
엔티티 설계시 주의점  (0) 2022.06.19
도메인 분석 설계  (0) 2022.06.19
JPA와 DB 설정, 동작확인  (0) 2022.06.19
View 환경 설정  (0) 2022.06.19

+ Recent posts