본문 바로가기

백엔드/JPA

JPA 엔티티 매핑2

일대다 단방향 매핑

이전 정리글에서는 단방향 매핑은 보통 연관관계의 주인이 n쪽이므로 다대일 단방향 매핑에 대해서만 정리했다. 하지만 일대다 단방향도 가능하다고 한다. 

우선 일대다에서는 객체의 데이터가 변경되면 디비에서 해당 엔티티의 테이블이 아닌 상대방 쪽의 테이블에 데이터가 수정된다. 무슨말인지 예시를 통해 보면.

만약 Team의 members에 새로운 멤버를 하나 추가한다고 하면 디비에서는 Team 테이블이 아닌 Member 테이블에 값이 추가되거나 변경된다는 말이다.

@Entity
@Getter @Setter
public class Book {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

@Entity
@Getter @Setter
public class BookStore {
    @Id @GeneratedValue
    private Long id;

    @OneToMany(cascade=CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name="mybook")
    private List<Book> booklist = new ArrayList<>();
}


//메인 함수 코드
Book b = new Book();
b.setName("dfb");
em.persist(b);

BookStore bs = new BookStore();
bs.getBooklist().add(b);
em.persist(bs);

위의 코드를 실행하면 결과는 insert문 2개에 update문 1개가 나간다. 만약 다대일 양방향이었다면 insert 2개만 나갔을 것이다. update문이 추가된 이유는 BookStore에 Book이 추가될때 BookStore가 아닌 Book 테이블이 변경되기 때문이다. 결국 객체와 테이블의 차이에서 오는 결과이다.

일대다 단방향 매핑은 엔티티가 관리하는 외래키가 다른 테이블에 있고, update 쿼리가 추가로 나간다는 단점이 있다. 일대다 단방향 매핑보다는 다대일 양방향 매핑을 쓰는 것이 좋다. 일대다 양방향 매핑은 가능하긴 하지만 실무에서는 많이 쓰이지 않는다고 한다. 그래도 방법을 한번 정리해보자면 양쪽 객체에 @JoinColumn을 주되 n 쪽에다가 @JoinColumn의 옵션으로 updatable=false, insertable=false 옵션을 주면 n 쪽에서는 읽기만 가능하게 된다.

 

일대일

일대일은 주 테이블이나 대상 테이블 중에 외래키를 어디다 둘 지 선택할 수 있다. 비교적 가장 간단하다.
연관관계의 주인이 될 객체 쪽에다가 @OneToOne과 @JoinColumn을 달아주면 된다. 양방향으로 하고 싶다면 반대편 테이블에는 @OneToOne(mappedBy="XXX") 를 주면 이 테이블은 읽기전용으로 되면서 양방향매핑이 이뤄진다.

일대일은 주 테이블에 외래키를 두는것이 JPA 매핑하기 가장 편리한 방식이다. 다만 값이 없으면 외래키에 Null이 들어가는 것을 허용해야하는 문제가 발생한다.

대상 테이블에 외래키를 둔다면 Null을 허용해야하는 상황이 발생하지 않기 때문에 데이터베이스 입장에서는 좋은 방식이다. 하지만 이 경우에는 주 테이블에서 대상 테이블에 접근하는 경우가 많아지만 양방향으로 매핑을 해야한다. 그리고 또 다른 단점으로는 프록시의 기능이 의미가 없어진다는 점이다.

지연로딩으로 Member를 조회했을때 디비 입장에서는 Locker가 있는지 없는지는 Member 테이블만 봐서는 모르기 때문에 Locker 테이블을 봐야한다. 그러므로 어차피 쿼리가 나가기때문에 프록시를 쓸 이유가 없어진다.

 

다대다

객체는 컬렉션을 통해 다대다 관계가 가능하다. 하지만 테이블은 그런게 불가능해서 가운데에 테이블을 하나 두어야한다. 

// Product 클래스
@ManyToMany(mappedBy = "products")
private List<Member> members = ArrayList<>();

// Member 클래스
@ManyToMany
@JoinTable(name="MP_TABLE")
private List<Product> products = new ArrayList<>();

이렇게 해두면 중간에 MP_TABLE이란 테이블이 생긴다. 해당 테이블은 Member와 Product의 키를 외래키로 들고 있다. 하지만 문제는 연결된 테이블에 어떤 데이터를 추가하는 것이 불가능하기 때문에 거의 쓰이지 않는다.

 

마무리

다대다 결론 : 안쓰인다! 일대다 다대일 로 풀어내자.....

 

관련글

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard 를 공부하며 정리한 글입니다.

 

'백엔드 > JPA' 카테고리의 다른 글

JPA 값타입  (0) 2021.08.08
JPA 프록시와 영속성 전이  (0) 2021.08.06
JPA 엔티티 매핑1  (0) 2021.07.29
스프링부트 테스트 영속성과 JPQL  (0) 2021.07.27
JPA와 영속성  (0) 2021.07.27