Programming-[Backend]/JPA

[JPA활용-1] 2. 엔티티 작성 : 계층구조 연관관계 매핑 등

컴퓨터 탐험가 찰리 2021. 11. 8. 21:48
728x90
반응형

 

 

 

활용편은 대부분 아는 내용이라, 차례대로 모두 정리하지 않고 새롭게 알게된 내용들만 일부 참조하여 작성한다.

 

 

 

테이블명 단수형, 복수형 문제

 

복잡한 문제로 논란이 많지만, 테이블의 레코드 1개가 가지는 의미를 생각하여 단수로 작성한다. 테이블명이 명확한 단어로 작성되는 경우도 있지만, H1KQLW와 같은 코드로 작성되는 경우도 있다고 한다. 이런 경우에는 복수형으로 만들기가 곤란하다. 다만, order와 같은 일부 예약어는 단수로 작성 시 문제가 되므로 복수로 작성하여 혼용되기도 한다.

 

사용하는 DB의 종류, 프로그래밍 언어, 상황에 따라 다르겠지만 강의에서는 결국 프로젝트나 회사 단위에서 지정하기 나름이라고 한다. 일관성이 중요하다.

 


 

 

Getter는 열되, Setter는 무조건 열지는 않는다.

 

Entity 마다 무조건 @Getter, @Setter를 추가하는 방식을 사용하지 않는 것을 권장한다. Entity를 통해 바로 DB에 접근하기 때문에 실수로라도 set을 해버리는 상황이 없도록 변경이 필요하지 않은 Entity라면 @Setter를 미리 추가해놓지는 않는다고 한다.

 

개인적인 생각으로는 테이블과 매칭되는 Entity의 값을 전혀 변경하지 않는 경우가 있을까 싶긴하다. 뭔가 비즈니스 로직이 들어가는 Entity 및 테이블이라고 생각되면 미리 @Setter를 추가해두고, 단순히 몇개 안되는 데이터가 참조로 사용되는 Entity 및 테이블이라면 @Getter만 적용하는 것이 맞지 않을까 싶다.

 

 

 


 

intelliJ : Project Module에 JPA 추가 여부 확인

 

intelliJ에서 JPA를 Project structure - module 부분에 추가해줬는지 확인한다. intelliJ의 enterprise 버전(유료 버전)에서는 JPA 모듈이 추가되면 Entity및 연관관계 매핑이 제대로 작성되었는지 검사하는 기능 등을 제공해준다.

 

강의 대비해서 intelliJ 버전이 올랐는지, application.yml에서 자동으로 JPA를 검사하여 setting에 넣어준 것을 확인할 수 있었다.

 

 


 

다대다 연관관계 매핑

 

다대다 연관관계는 중간 테이블이 생기고, 이 중간 테이블에 지정된 컬럼 외에 더 이상의 컬럼을 추가할 수 없으므로 안티 패턴이라 사용하면 안된다고 기본편에서 배웠다. 다만 이 강의에서는 어떻게 사용하는지 예시를 보여주긴 한다.

 

Item과 Category가 다대다 연관관계를 가지는 경우, 아래와 같이 작성한다. 두 쪽 다 List 형태로 작성하되, 한쪽에는 @JoinTable 어노테이션을 붙여주어 중간 테이블을 생성한다는 것을 표시하고, @JoinColumn은 category 에서의 id값, inverserJoinColumns 는 반대편 item에서의 id값을 표시해준다.

 

반대편인 item에서는 일대다 연관관계에서와 같이 mappedBy로 표기해준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//카테고리
 
@Entity
@Getter
@Setter
public class Category {
 
  @Id
  @GeneratedValue
  @Column(name = "category_id")
  private Long id;
 
  private String name;
 
  @ManyToMany
  @JoinTable(name = "category_item",
  joinColumns = @JoinColumn(name = "category_id"),
          inverseJoinColumns = @JoinColumn(name = "item_id"))
  private List<Item> items = new ArrayList<>();
}
 
//아이템
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter
@Setter
public abstract class Item {
 
  @Id
  @GeneratedValue
  @Column(name = "item_id")
  private Long id;
 
  private String name;
  private int price;
  private int stockQuantity;
 
  @ManyToMany(mappedBy = "items")
  private List<Category> categories = new ArrayList<>();
}
cs

 

 

 


 

[중요] 계층 구조 연관관계 매핑

 

카테고리는 일반적으로 계층 구조를 갖는다. 식품 -> 육류 -> 돼지, 소, 닭 등... 이런 구조를 만들기 위해서 아래와 같이 parent, child 연관관계를 만들면 된다.

 

Category 클래스 내부에 Category 클래스를 하나의 필드값으로 넣고, @ManyToOne 관계로 잡아준다. 이 필드가 parent가 된다. 그리고 @OneToMany로 child를 List 형태로 잡아준다. 이런식으로 설계하면, 예를 들어 Category의 1개 레코드의 parent 값은 "육류"가 되어 parent_id 값으로 저장되고, 이 parent가 List로 된 '돼지, 소, 닭' 등을 가지게 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Entity
@Getter
@Setter
public class Category {
 
  @Id
  @GeneratedValue
  @Column(name = "category_id")
  private Long id;
 
  private String name;
 
  @ManyToMany
  @JoinTable(name = "category_item",
  joinColumns = @JoinColumn(name = "category_id"),
          inverseJoinColumns = @JoinColumn(name = "item_id"))
  private List<Item> items = new ArrayList<>();
 
  @ManyToOne
  @JoinColumn(name = "parent_id")
  private Category parent;
 
  @OneToMany(mappedBy = "parent")
  private List<Category> child = new ArrayList<>();
}
cs

 

 

 

 


 

[중요] 임베디드 타입 : 값 타입은 변경 불가능하도록 설계

 

JPA 스펙상 embbeded 타입은 아래와 같이 두면 안되고, 기본 생성자를 protected로 설정해주어야 한다.

 

1
2
3
4
5
6
7
8
9
@Embeddable
@Getter
public class Address {
 
  private String city;
  private String street;
  private String zipcode;
 
}
cs

 

이는 JPA 구현 라이브러리가 객체를 생성할 때 리플렉션 같은 기술을 사용하기 위해 지원해야되기 때문이라고 한다. 값 타입이라서 변경 불가능하도록 private으로 설정하면 안되고, protected 정도로 설정한다고 한다.

 

리플렉션 등은 아직 이해가 잘 안된다. 추후에 다시 이해할 수 있도록 하자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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;
  }
}
cs

 


 

참조

 

1. 인프런_실전! 스프링 부트와 JPA 활용1_김영한 님 강의

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1/dashboard

728x90
반응형