Hibernate/JPA(EntityManager)

[JPA] @OneToMany 단방향 / 양방향 - 외래키 관리 이해한 내용 정리

유혁스쿨 2023. 6. 29. 11:33
728x90
반응형
@Entity(name = "USERs")
@Setter
@Getter
@ToString
public class User {
    @Id @GeneratedValue
    @Column(name = "USER_ID")
    private Long id;
    private String username;
}
@Entity
@Setter
@Getter
@ToString
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String teamname;
}

 

@OneToMany 단방향

1:M관계에서는 연관관계의 주인이 1쪽으로 잡혀버린다.
외래키 자체는 M쪽에 존재하는데 외래키를 M이 관리하지 않고 1이 관리하게 된다.


@JoinColumn("연관관계 1 엔티티 PK") 여기서 말하는 pk는 자기자신을 가리킨다.
List 즉 M형태의 컬렉션 타입 필드를 선언해준다.

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<User> users = new ArrayList<>();


1에서 M 즉, Team에서 User에 존재하는 TEAM_ID 외래키를 관리해야 하는데

데이터베이스 테이블에는 M쪽에 FK가 생성이되지만

M의 Entity에는 필드로서 존재하지 않기 때문에 그것에 대한 관리 자체를 M쪽에서 할 수 가 없게된다.
따라서 1에 선언한 컬렉션에 M 객체를 추가해 줘야만 JPA가 인식하고 FK를 관리할 수 있게 된다.

@SpringBootTest
@Transactional
public class JpaTest {
    @PersistenceContext
    EntityManager em;
    
    @Test
    public void OneToXTest() {
        User user = new User();
        user.setUsername("userA");
        em.persist(user);
        
        Team team = new Team();
        team.setTeamname("teamA");
        team.getUsers().add(user); // DB 관점에서 User에 team_id 외래키가 추가되고 Update쿼리를 통해 관리된다.
        em.persist(team);

        em.flush();
        em.clear();

        User findUser = em.find(User.class, user.getId());
        System.out.println("findUser = " + findUser);
        Team findTeam = em.find(Team.class, team.getId());
        System.out.println("findTeam = " + findTeam);
        System.out.println("findTeam.getUsers() = " + findTeam.getUsers());
    }
    
}

[콘솔 출력 내용]

update
        users 
    set
        team_id=2 
    where
        user_id=1

이때 FK에 대한 관리는 insert쿼리로 나가지 않고 update 쿼리를 통해 관리된다.
update쿼리가 나가는 이유는 앞서 설명한대로, M:1관계에서는 M쪽에 1에대한 정보를 세팅해줌으로써 FK가 관리가 되는데, M쪽에 FK 관리를 할 수 없기 때문에 1에대한 정보를 세팅할 수가 없게 된다.

team.getUsers().add(user);

결과적으로 위 코드와 같이 컬렉션에 m 객체를 추가함으로써 JPA에서는 추가된 m 객체에 현재 1 의 대상 외래키값을 Update 처리하는걸로 이해할 수 있겠다.

 

@OneToMany 양방향

JPA 스펙상 공식적으로 존재하지 않지만 가능은 하다.

    @ManyToOne
    @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false) //JPA가 인식하고 
    private Team team;

User 객체에서 team객체를 Team 객체의 TEAM_ID PK기준으로 ForeignKey 연관관계 매핑은 하되,

insert와 update를 하지않는 읽기전용 필드로만 지정하는것이다.

 

앞서 ManyToOne 단방향에서 Team을 연관관계의 주인으로 만들었고, 위 같이 선언하게 되면 User에도 연관관계의 주인처럼 만들었지만, 읽기 전용으로 설정해버린것이다.

이렇게 되버리면 사실상 양방향 매핑한 것과 똑같이 된다.

관리는 Team으로 하고 User에서도 읽기가 가능하도록

JPA스펙상 1:M 양방향 관계는 공식적으로 존재하지 않지만 위와 같이 설정할 수 있다.

 

실무에서 복잡하게 하다 보면 위와 같이 OneToMany 양방향을 통해 One에서 읽을 수 있도록 설정하는 전략이 한번씩 필요할 때가 있다.

728x90
반응형