[인프런] [Spring Data JPA] [섹션2] 엔티티 정의 후 테스트

mutexlocking·2022년 8월 1일
0

이번 섹션은 사실상 JPA를 활용하는 내용보다는 단순히 엔티티 클래스를 정의하는것이 주된 내용이어서 따로 정리를 하지 않으려고 했다.

그러나 엔티티를 혼자 정의해보면서 역시 머리속에서 흐릿해졌거나 ,
제대로 정리되지 않은 부분들이 적지 않은 것 같아,
나중에 다시 복습할 겸 정리를 한다. (결국 제대로 내 지식이 안되었기 때문)

[주요 복습 내용]
1. 엔티티 클래스를 정의하면서 기억해야할 내용들
: 롬복의 어노테이션 @ToString, 연관관계는 반드시 지연로딩! 근데 이유가 뭐였지?
2. 엔티티 클래스의 필드 타입을 Primitive 타입으로 할지 vs Wrapper 클래스 타입으로 할지
: 그 정확한 기준은 뭐고 , 그래서 난 어떤걸 선택했는지
3. 연관관계의 주인 vs 거울
: 주인이냐 , 거울이냐 에 따라 어떤 차이가 있는지

1. [엔티티 클래스를 정의하면서 다시 정리할 부분]

  • 새로운 어노테이션 - 롬복의 @ToString
    • 이 엔티티 객체의 toString() 호출시 어떤 필드가 출력될지를 명시해줌
    • @ToString(of = {"id", "username", "age"}) 라고 선언하면
      id , username, age 필드만을 출력하도록 toString()이 알아서 오버라이딩.
    • 단 주의할 점은 연관관계가 맺어진 필드는 선언되면 안된다는 점!
      -> 그 경우 Member와 Team간에 서로를 무한히 호출하는 loop를 타게 됨.
  • 연관관계의 주인이 되는 필드를 명시하는 @JoinColumn
    • 이 어노테이션을 통해서도 PK의 @Column과 같이
      명시적인 FK 이름을 정해주는게 좋음
    • 우리의 경우는 Member에 Team에 대한 FK가 있으므로
      : @JoinColumn(name = "team_id")
  • ~ToOne 관계는 명시적으로 [지연로딩]을 지정해줘야 함
    • ~ToMany와 달리 디폴트가 [즉시로딩]이기 때문
    • 그 이유
      : [즉시로딩] 은 성능최적화 시키기 매우 어렵기 때문.
      [지연로딩] 이 성능최적화에 더 수월하다!
      따라서 [모든 연관관계] 는 [반드시 지연로딩]으로!

[위의 내용들을 고려하여 정의한 실제 Member, Team 엔티티 클래스]

@Entity
@Getter @Setter
@Table(name = "member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "username", "age"})
public class Member {

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

    private String username;

    private int age; // DB상에 null 이 들어가면 안되는 필드이므로 (null 원천 봉쇄)-> Primitive 타입 사용


    @JoinColumn(name = "team_id")
    @ManyToOne(fetch = FetchType.LAZY)
    private Team team;


    /** [생성자] */
    public Member(String username) {
        this(username, 0);
    }

    public Member(String username, int age) {
        this(username, age, null);
    }

    public Member(String username, int age, Team team) {
        this.username = username;
        this.age = age;

        if(team != null){
            changeTeam(team);
        }
    }

    /** [연관관계 편의 메서드] : 이름은 변경메서든데 , 연관관계 편의 메서드로 사용하심 */

    public void changeTeam(Team team){
        this.team = team;
        team.getMembers().add(this);
    }
}
@Entity
@Getter @Setter
@Table(name = "team")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "name"})
public class Team {

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

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    public Team(String name) {
        this.name = name;
    }
}

2. [엔티티 필드 타입의 결정 기준]
: Primitive 타입으로 할지 vs Wrapper 클래스 타입으로 할지

  • 결론부터 말하면

    • 컬럼 값이 Null이어도 되면 Wrapper 클래스 타입을 써도 되고
    • 컬럼 값이 반드시 Null이 아니어야 한다면 Primitive 타입을 써서
      Null을 원천 봉쇄 한다.
  • 그러나 PK의 경우 JPA에서도 Long 타입을 권장한다

  • 관련 글은 https://velog.io/@d-h-k/JPA-Entity-Class-에서-Primitive-Type-을-써야할까-Wrapper-Class-를-사용해야할까 를 참고하면 좋을 것 같다!

  • 어쨌든 그래서 Member의 age 필드는

    • Member라면 반드시 가져야 할 값이라는 측면에서
    • null이 안되도록 하는게 더 적합할 것 같다!
    • 그래서 Integer보단 int를 사용한다!

3. [연관관계 주인 vs 거울]
: 그 본질적인 차이가 뭐였지?

  • 연관관계 주인은
    • 실질적으로 FK와 대응되는 필드이다.
    • 이말은 즉 FK값의 Read/Write 가 모두 가능하다는 의미이다.
  • 반면 연관관계의 거울은
    • 엔티티 객체 레벨에서 - DB의 FK로 인한 양방향 참조 를 가능하게 해주는 수단이다.
    • 여기서 핵심은 실질적인 FK와 대응되는 필드가 아니라는 점이고,
      이로인해 FK값의 Read만 가능하다는 제약이 생긴다.
      (핵심은 FK값을 Write하지 못한다는 점!)
profile
개발자가 되고자 try 하는중

0개의 댓글