새소식

xub2의 개발 새발 🐾
💽 Database/JPA

[JPA] 연관관계 매핑 - 2 : 객체지향 모델링 / 단방향 연관관계

2025. 9. 15. 11:57

  • -

지난 포스팅에서 테이블 지향적 (DB 테이블) 설계를 하면 어떤 문제가 발생하는지 알아보았다.

지난 포스팅에서 코드를 리팩토링하여 엔티티가 FK 값을 가지고 있는 것이 아닌 , 실제 객체를 가르키고 있도록 해보자.

이번 예시는 Member 와 Team이 존재한다.

한명의 member은 하나의 team에 속할 수 있고 / 하나의 team에는 여러 member가 속할 수 있다.

즉 member와 team은 1:N 관계이다.

 

그리고 처음 설명한 것 처럼 member가 team의 FK를 들고 있는 대신 실제 Team 엔티티를 필드로 가지고 있도록 해보자.

단방향 매핑

@Entity
@Getter @Setter
public class Member {

    @Id @GeneratedValue
    @Column(name = "member_id")

    private Long id;

    private String username;

    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
    
    

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "team_id")
    private Long id;
    private String name;
}

member 엔티티의 team 필드 참조값과 실제 테이블 TEAM_ID 컬럼을 매핑해야한다. -> 사실 이렇게만 보면 뭔말인지 잘 모르겠음

쉽게 생각해보자.

1. 물리적 DB에 member 테이블(member_id, name, team_id)과 team 테이블(team_id, name)이 있다. 

2. 엔티티의 모든 필드 (@Transient 제외)는 테이블의 컬럼과 1:1 매칭이 되어야 한다.

3. 검색 조건이 홍길동이란 회원의 정보와 그 소속 팀 이름을 조회한다고 가정하자

4. 그럼 원래 SQL은 다음과 같을 것이다

SELECT *
FROM member m JOIN team t ON m.team_id = t.team_id
WHERE m.username = '홍길동';

5. 여기서 테이블을 조인하려면 member 테이블 쪽에 team테이블의 FK(team_id)가 필요하다. 

6. 다시 member 엔티티 코드를 보면 @JoinColumn 에노테이션이 있는데, 해당 필드를 team_id를 FK로 갖는 조인 컬럼으로 쓰겠다는 소리다.

7. 최대한 내가 이해하기 쉽게 로직을 정리하면 테이블의 하나의 로우는 하나의 객체이다 + 그 객체를 자바 코드에선 객체로 사용하고, 물리 DB에서는 FK 값으로 처리한다. = 자바 코드 Team 과 DB의 team_id 가 매핑되는 것

8. 추가적으로 두 테이블간의 관계를 @ManyToOne , @OneToMany .. 등등을 통해 JPA에게 알려줘야함.

 

테스트코드 작성

@Test
public void 회원_저장() throws Exception {
    EntityTransaction tx = em.getTransaction();
    tx.begin();

    try{

        Team team = new Team();
        team.setName("알나스르");
        em.persist(team);

        Member member = new Member();
        member.setUsername("로나우도");
        member.setTeam(team);
        em.persist(member);

        tx.commit();

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

@Test
public void 회원_조회() throws Exception {
    EntityTransaction tx = em.getTransaction();
    tx.begin();
    
    try{
        Team team = new Team();
        team.setName("씨우ㅜㅜ");
        em.persist(team);

        Member member = new Member();
        member.setUsername("베일");
        member.setTeam(team);
        em.persist(member);

        Member findMember = em.find(Member.class, member.getId());
        Team findTeam = findMember.getTeam();

        assertEquals("씨우ㅜㅜ", findTeam.getName());

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

첫번째 테스트를 통해, 이제 더이상 team_id를 넘길 필요 없이 team 객체를 넘기기만 하면 자동으로 JPA가 처리해준다.

두번째 테스트를 통해 이제 더이상 em.find로 엔티티를 찾아와 id값을 반환받고 그 id를 기준으로 다시 find 하여 여러번의 쿼리를 날릴 필요 없이 member만 찾아오면 해당 member의 연관 관계를 보고 getTeam() 메서드만으로 쉽게 team 정보를 찾아올 수 있게되었다.

추가적으로 위 작업들은 다 영속성 컨텍스트 안에 들어있는 데이터기 때문에 쓰기지연 최적화까지 가능하다.

결정적으로 코드가 상당히 객체 지향적으로 변경되었다 !

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.