[ JPA ] 5-4. Entity Relations ( N:1 @ManyToOne )
백엔드/JPA

[ JPA ] 5-4. Entity Relations ( N:1 @ManyToOne )

Reference. 한 번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지 Online

이전 글

1. N:1

  • @OneToMany에서 참조하는 값은 One에 해당하는 PK값을 Many쪽에서 FK로 가짐
    UserHistory 테이블에서는 User에 Id값을 가짐
  • 일반적인 상황에서는 @ManyToOne이 깔끔하게 엔티티를 구성
    → 해당 엔티티가 필요로 하는 FK 엔티티가 함께 가지고 있기 때문
UserHistory.java
@Entity
...
public class UserHistory extends BaseEntity{
		@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String email;
		
		@ManyToOne
    private User user;
}
User.java
  • @ToString.Exclude를 선언하지 않으면 결과 확인 순환참조로 에러 발생
@Entity
...
public class User extends BaseEntity{
		...

		@OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_Id", insertable = false, updatable = false)
    @ToString.Exclude
    private List<UserHistory> userHistories = new ArrayList<>();
}
UserEntityListener.java
public class UserEntityListener {
    @PostPersist
    @PostUpdate
    public void postPersistAndPostUpdate(Object o){
        UserHistoryRepository userHistoryRepository = BeanUtils.getBean(UserHistoryRepository.class);

        User user = (User) o;
        UserHistory userHistory = new UserHistory();
        userHistory.setName(user.getName());
        userHistory.setEmail(user.getEmail());
        userHistory.setUser(user);

        userHistoryRepository.save(userHistory);
    }
}
실행결과
@Test
void userRelationTest(){
	...
	System.out.println("userHistory getUser() : " + userHistoryRepository.findAll().get(0).getUser());
}

 //콘솔 값
 userHistory getUser() : User(super=BaseEntity(createdAt=2021-07-28T21:16:57.792881, updatedAt=2021-07-28T21:16:58.014881), id=6, name=Kim, email=kim@gmail.com, gender=MALE)
  • JPA는 연관된 객체를 FK로 조회하는 것이 아니라 getter를 통해 가져오게 됌
  • @OneToMany, @ManyToOne, 양방향 중 선택은 어느 엔티티에 연관 엔티티가 필요한지 알아야함
    • UserHistory에 값을 조회해서 보면서 User조회할 일은 거의 없음
    • User에서 변경이력을 보기 위해서 UserHistory조회할 일은 많음
    • User에서 @OneToMany를 활용해서 UserHistory연관관계를 맺는 것이 나은 방법

2. ERD → Entity 옮기기

<ERD>

 

<Entity>

book.java
...
public class Book extends BaseEntity{
		...
		@OneToOne(mappedBy = "book")
    @ToString.Exclude
    private BookReviewInfo bookReviewInfo;

    @OneToMany
    @JoinColumn(name="book_id")
    @ToString.Exclude
    private List<Review> reviews = new ArrayList<>();

    @ManyToOne
    @ToString.Exclude
    private Publisher publisher;
}
BookReviewInfo.java
...
public class BookReviewInfo extends BaseEntity{
		@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(optional = false)
    private Book book;

    private float averageReviewScore;

    private int reviewCount;
}
Publisher.java
...
public class Publisher extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany
    @JoinColumn(name = "publisher_id")
    private List<Book> books = new ArrayList<>();
}
Review.java
...
public class Review extends BaseEntity{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    private String content;

    private float score;

    @ManyToOne
    private User user;

    @ManyToOne
    private Book book;
}
User.java
...
public class User extends BaseEntity{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NonNull
    private String name;

    @NonNull
    private String email;

    @Enumerated
    private Gender gender;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_Id", insertable = false, updatable = false)
    @ToString.Exclude
    private List<UserHistory> userHistories = new ArrayList<>();

    @OneToMany
    @JoinColumn(name = "user_id")
    @ToString.Exclude
    private List<Review> reviews = new ArrayList<>();
}
UserHistory.java
public class UserHistory extends BaseEntity{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String email;

    @ManyToOne
    private User user;
}

3. 정리

  • 예제에선 @OneToMany, @ManyToOne 모두 적용, 둘 중에 하나만 선택해도 되는 듯?
    → 실습 및 공부를 통해서 확인이 필요한 부분
  • @OneToMany
    • 실제 데이터베이스에서는 컬럼으로 존재하지 않음 (book_id)
    • @JoinColumn: 연관된 엔티티FK를 지정 (안하면 N:N 테이블 생성)
  • public class Publisher extends BaseEntity { ... @OneToMany @JoinColumn(name = "publisher_id") private List<Book> books = new ArrayList<>(); }
  • @ManyToOne
    • 실제 데이터베이스에서 FK로 존재 (book_id, user_id)
     
  • public class Review extends BaseEntity{ ... @ManyToOne private User user; @ManyToOne private Book book; }