1:N 단방향 연관 관계

타키탸키·2023년 4월 6일
0

WEB

목록 보기
4/4

1:N 단방향 연관 관계에서의 엔티티와 테이블 매핑

데이터베이스 관례상 N에서 1을 참조하기 위한 FK를 정의해야 한다. 이때, FK는 1의 PK이다.

예를 들어, 하나의 Album이 여러 Song을 가지는 1:N의 관계라고 가정하자. Album 클래스는 Song 클래스의 리스트를 멤버로 가지고 있다. 그럼 엔티티와 테이블에 매핑을 고려해서 Album 테이블에도 Song을 참조하기 위한 컬럼이 있어야 할까?

꼭 그렇지는 않다. 만약, Album과 Song이 1:N이며 단방향 관계라면 다음과 같은 양상을 보인다.

==== << 엔티티 >> ====

@Entity
public class Album {
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long albumId;
    private String albumName;
    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "album_id")
   	private List<Song> songs;
}

@Entity
public class Song {
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long songId;
	private String songName;

==== << 테이블 >> ====

Album Table:
+-----------+-------------+
| album_id  | album_name  |
+-----------+-------------+
|     1  	|  Love       |
|     2  	|  Happiness  |
|     3  	|  Dream      |
+-----------+-------------+

Song Table:
+---------+--------------+----------+
| song_id |  song_name   | album_id |
+---------+--------------+----------+
|    1    |  Love Song   |    1     |
|    2    |  I Love U    |    1     |
|    3    |  Happiness   |    2     |
|    4    |  Dreams      |    3     |
+---------+--------------+----------+

위 예시를 보면 Album 테이블에 Song과 관련된 컬럼이 없다. 오히려 Song 쪽에 Album의 PK인 album_id에 대한 필드가 있다. 왜 그런걸까?

엔티티와 테이블은 서로 다른 개념이므로 엔티티와 테이블 간에 필드가 반드시 1:1로 매핑되지는 않는다. 따라서, Alubm 엔티티가 Song 엔티티를 참조하고 있다고 해서 반드시 Album 테이블이 Song 테이블 관련 컬럼을 가져야 하는 것은 아니다.

관례상 데이터베이스는 N 테이블에 FK가 존재한다. 이에 반해, 엔티티 관점에서는 1:N 단방향 관계가 성립하려면 1의 엔티티가 N 엔티티의 컬렉션을 필드로 가져야 한다. 바로 이 지점에서 엔티티와 테이블 간 차이가 발생하는 것이다.

1:N 단방향 연관 관계의 문제점

1:N 단방향 관계가 가지는 구조적 문제는 쿼리에도 영향을 끼친다. 만약 1 엔티티에서 참조하고 있는 필드인 N 엔티티 값을 수정한다고 해보자.

try {
    Song song = new Song();
    song.setSongName("new song");

    em.persist(song);

    Album album = new Album();
    album.setAlbumName("new album");
    album.getSongs().add(song);

    em.persist(song);
    
    tx.commit();
} catch (Exception e) {
    tx.rollback();
} finally {
    em.close();
}

위 코드에는 Album 객체의 필드인 songs를 업데이트 하는 로직이 구현되어 있다. 문제는 데이터베이스에 변경 사항을 반영하기 위해 전송되는 쿼리에서 확인할 수 있다.

insert into Song (songId, songName)
values (null, ?)
            
insert into Album (albumId, albumName)
values (null, ?)
            
update Song set ALBUM_ID=? where songId=?    

수정된 엔티티는 Album인데 실제 쿼리는 Song 테이블에 전송된다.

단순한 구조에서는 상관 없지만 실무에서는 여러 테이블이 다양한 관계로 엮여서 돌아가는만큼 운영에 어려움이 발생할 수 있다.

profile
There's Only One Thing To Do: Learn All We Can

0개의 댓글