본문 바로가기

Spring/JPA

JPA- 연관관계 매핑 주의점

상황

 Member:Team = N:1의 관계이다. 양방향 연관 관계의 주인은 Member.team이다.

잘못된 경우(연관관계 주인이 아닌 곳에서 값을 입력하는 경우)

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try{
            Member member = new Member();
            member.setUserName("member1");
            em.persist(member);

            Team team = new Team();
            team.setName("TeamA");
            //역방향(연관관계 주인이 아닌 방향)만 연관관계 설정
            team.getMembers().add(member); 
            em.persist(team);

            em.flush();
            em.clear();
            
            tx.commit();
        } catch(Exception e) {
            tx.rollback(); //오류 발생 시 롤백
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}
실행 결과

 지금 위의 경우는 가짜 매핑된 team.members에서 update를 하고 있다. 가짜 매핑된 부분에서는 뭔 짓을 하든 DB에는 반영이 되지 않는다. 가짜 매핑된 team.members에 member를 add()하더라도 member.team에는 연관관계가 매핑되지 않아MEMBER 테이블의 TEAM_ID가 null인 것을 볼 수 있다.

 

올바른 경우

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try{
            Team team = new Team();
            team.setName("TeamA");
            em.persist(team);

            Member member = new Member();
            member.setUserName("MemberA");
            // 연관 관계주인에서 매핑
            member.setTeam(team);
            em.persist(member);

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

			// team 조회
			Team findTeam = em.find(Team.class, team.getId());
            List<Member> members = findTeam.getMembers();
            // team.members 출력
            for (Member m : members) {
                System.out.println("m = " + m.getUserName());
            }
            
            tx.commit();
        } catch(Exception e) {
            tx.rollback(); //오류 발생 시 롤백
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}

실행 결과

결론

 양방향 연관관계인 경우에는 순수한 객체 관계를 고려하여 양쪽모두에 값을 넣어주자.

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUserName("MemberA");
member.setTeam(team);  // 값 주입
em.persist(member);

team.getMembers().add(member);  // 값 주입

tx.commit();

근데 항상 이렇게 일일히 넣어주기 힘드니까 연관관계 편의 메소드를 만들자.

연관관계 편의 메소드

package hellojpa;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;

@Entity
@Getter
@Setter
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME", nullable = false)
    private String userName;
    
    @ManyToOne 
    @JoinColumn(name = "TEAM_ID") 
    private Team team;
    
    // 연관관계 편의 메소드
    public void changeTeam(Team team) {
        this.team = team;
        team.getMembers().add(this); //setTeam()시 자동으로 반대편에도 값이 주입됨
    }
}

'Spring > JPA' 카테고리의 다른 글

JPA- 상속관계 매핑  (0) 2023.03.12
JPA- 다대일, 일대일  (0) 2023.03.08
JPA- 연관관계 매핑  (0) 2023.03.05
JPA- 중요! 엔티티 매핑  (0) 2023.02.28
JPA- flush와 영속성 관리  (0) 2023.02.28