[JPA & Hibernate] Bidirectional & Unidirectional Relationships

원알렉스·2020년 8월 26일
0

JPA

목록 보기
15/16
post-thumbnail

🚀 단방향과 양방향이란?

  • 단방향 관계 ➡
    • 두 엔티티가 연관 관계를 맺고 있을 때, 한 쪽의 엔티티만 다른 쪽을 참조하고 있는 것을 의미합니다.
  • 양방향 관계 ↔
    • 두 엔티티가 연관 관계를 맺고 있을 때, 양 쪽이 서로를 참조하고 있는 것을 의미합니다.

데이터베이스 모델에서는 관계를 맺어주기만 하면 자동으로 양방향 관계가 형성이 되지만(사실 외래키로 죠인만 해주면 되므로 방향이 없다고 볼 수 있음), 객체지향 모델에서는 구현하고자 하는 서비스에 따라 단방향 관계인지 혹은 양방향 관계인지 적절한 선택을 해야 합니다.

엄밀히 말하면 양방향 관계는 서로 다른 단방향 연관 관계 2개를 로직으로 양방향인 것처럼 보이게 할 뿐이라서 양방향 관계는 존재하지 않는다고 할 수 있습니다.

양방향 관계를 매핑할 경우에는 한 쪽 엔티티에 mappedBy를 통해 연관 관계의 주인을 지정해 주어야 합니다.

✔ 객체 연관관계 vs 테이블 연관관계

  • 객체는 참조(주소)를 통해 연관 관계를 맺습니다.
  • 테이블은 외래키를 통해 연관 관계를 맺습니다.
  • 참조를 사용하는 객체의 연관 관계는 단방향입니다(방향성 존재).
  • 외래키를 사용하는 테이블의 연관 관계는 양방향입니다(애초에 서로 외래키로 죠인이 가능하므로 사실상 방향성 없음).

연관관계의 주인과 mappedBy

양방향 매핑 규칙

  • 연관관계를 맺고 있는 두 엔티티 중에서 한쪽만을 주인으로 지정합니다.
  • 연관관계의 주인만이 외래키를 관리하게 됩니다.
    • 주인만이 데이터베이스에 접근하여 값을 등록, 수정, 삭제할 수 있습니다.
    • 주인이 아닌 객체가 값을 변경하더라도 데이터베이스에 영향이 없습니다.
    • 주인이 아닌 객체는 읽기만 가능합니다.
  • N(Many)쪽을 연관관계의 주인으로 지정합니다. 즉, 주인이 아닌쪽에 mappedBy 속성을 추가해줍니다.
@Entity
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @ManyToOne
    private BookStore bookStore;
    ...
}
@Entity
public class BookStore {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "bookStore")
    private List<Book> books = new ArrayList<>();
    // NullPointerException을 방지하기 위해 초기화도 해줍니다
    ...
}

✔ 양방향 관계 사용시 흔히 하는 실수

주인이 아닌쪽에 새로운 데이터를 추가만하고 주인쪽의 외래키를 설정하지 않은 경우

@Entity
public class BookStore {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "bookStore")
    private List<Book> books = new ArrayList<>();
    // NullPointerException을 방지하기 위해 초기화도 해줍니다
    
    public void addBook(Book book) {
        // book.setBookStore(this);
        this.books.add(book);
    }
    ...
}
@Test
@Transactional
void contextLoads() {
    BookStore bookStore = new BookStore();
	bookStore.setName("스프링 북스토어");
	bookStoreRepository.save(bookStore);
	LOGGER.info("Save Bookstore");

	Book book = new Book();
	book.setTitle("JPA & Hibernate");

	bookStore.addBook(book);
	LOGGER.info("Add a book to bookstore");

	bookRepository.save(book);
	LOGGER.info("Save book");
}

위의 코드에서처럼 BookStore 클래스 안에 있는 addBook() 메소드에서 주석된 코드를 실행하지 않고 테스트를 실행하면 Book 테이블에서 외래키인 book_store_idnull 값으로 저장이 됩니다.

즉, 관계를 추가할 때 주인이 아닌 쪽에만 추가를 하면 안되고 주인쪽에서도 추가를 해주는 작업을 해줘야 합니다.

그렇다면 주인인 쪽에만 추가하면되지 왜 데이터베이스에 반영도 안되는 쪽에도 추가를 하는지 의문이 생길 수도 있습니다.

이 질문은 JPA를 왜 사용하냐 ORM을 왜 사용하냐는 질문과 같다고 볼 수 있습니다. 객체 지향적으로 코드를 작성하기 위해서 JPA를 사용하는 것이고 객체 지향적인 관점에서 보면 당연히 연관관계에 있는 두 쪽 모두한테 관계를 추가해주는 것이 당연하다고 볼 수 있습니다.

profile
Alex's Develog 🤔

0개의 댓글