JPA

연관관계 매핑 기초

ewok 2023. 4. 12. 14:59
  • 방향
    • 단방향 : 회원 -> 팀 또는 팀 -> 회원 둘 중 한쪽만을 참조하는 관계
    • 양방향 : 회원 -> 팀, 팀 -> 회원 양쪽 모두 서로 참조하는 관계
    • 방향은 객체 관계에만 존재하고 테이블 관계는 항상 양방향이다.
  • 다중성 : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)
  • 연관관계의 주인 : 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 함

 

단방향 연관관계

다대일 단방향 관계

  • 회원과 팀
  • 회원은 하나의 팀에만 소속
  • 회원과 팀은 다대일 관계
@Entity
public class Member {

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

    private String username;

    //연관관계 매핑
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    //연관관계 설정
    public void setTeam(Team tead) {
    	this.team = team;
    }

    //Getter, Setter...
}
@Entity
public class Team {

    @Id
    @Column(name = "TEAM_ID")
    private String id;

    private String name;

    //Getter, Setter ...
}

객체 연관관계 : 회원 객체의 Member.team 필드 사용

테이블 연관관계 : 회원 테이블의 MEMBER.TEAM_ID 외래 키 컬럼 사용

 

  • @ManyToOne : 다대일 관계라는 매핑 정보
  • @JoinColumn(name = "TEAM_ID") : 외래 키 매핑할 때 사용

 

@JoinColumn

속성 기능 기본값
name 매핑할 외래 키 이름 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명
referencedColumnName 외래 키가 참조하는 대상 테이블의 컬럼명 참조하는 테이블의 기본 키 컬럼명
foreighKey(DDL) 외래 키 제약조건을 직접 지정 가능
테이블 생성할 때만 사용
 
unique
nullable
insertable
updatable
columnDefinition
table
@Column의 속성과 같음  

 

@ManyToOne

속성 기능 기본값
optional false로 설정하면 연관된 엔티티가 항상 있어야 함 true
fetch 글로벌 패치 전략을 설정 @ManyToOne=FetchType.EAGER
@OneToMany=FetchType.LAZY
cascade 영속성 전이 기능을 사용  
targetEntity 연관된 엔티티의 타입 정보를 설정
거의 사용하지 않음
 

 

 

연관관계 사용

저장

public void testSave() {

    //팀1 저장
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);

    //회원1 저장
    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1);
    em.persist(member1);

    //회원2 저장
    Member member2 = new Member("member2", "회원2");
    member2.setTeam(team1);
    em.persist(member2);
}

 

조회

//객체 그래프 탐색
Member member = em.find(Member.class, "member1");
Team team = member.getTeam();

//JPQL
String spql = "select m from Member m join m.team t where t.name=:teamName";

List<Member> resultList = em.createQuery(jpql, Member.class).setParameter("teamName", "팀1").getResultList();

for (Member member : resultList) {
	member.getUsername();
}

 

 

수정

//새로운 팀2
Team team2 = new Team("team2", "팀2");
em.persist(team2);

//회원1에 새로운 팀2 설정
Member member = em.find(Mebmer.class, "member1");
member.setTeam(team2);

 

연관관계 제거

Member member1 = em.find(Member.class, "member1");
member1.setTeam(null);

 

연관된 엔티티 삭제

  • 연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 함
  • 그렇지 않으면 외래 키 제약조건으로 인해 오류 발생
member1.setTeam(null);
member2.setTeam(null);
em.remove(team);

 

 

양방향 연관관계

  • 회원 -> 팀
  • 팀 -> 회원
@Entity
public class Member {

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

    private String username;

    //연관관계 매핑
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    //연관관계 설정
    public void setTeam(Team tead) {
    	this.team = team;
    }

    //Getter, Setter...
}

변경사항 없음

 

@Entity
public class Team {

    @Id
    @Column(name = "TEAM_ID")
    private String id;

    private String name;
    
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<Member>();

    //Getter, Setter ...
}

 

 

일대다 컬렉션 조회

Team team = em.find(Team.class, "team1");
List<Member> members = team.getMember();

for (Member member : members) {
	member.getUsername();
}

 

 

연관관계의 주인

  • 객체에는 양방향 연관관계라는 것은 없음
  • 서로 다른 단방향 연관관계 2개가 양방향처럼 보이는 것임
  • 두 객체 연관관계 중 하나를 정해서 테이블의 외래 키를 관리하게 해야 하는데, 이것을 연관관계의 주인이라고 함
  • 주인은 mappedBy 속성을 사용하지 않음
  • 주인이 아니면 mappedBy 속성을 사용하여 속성의 값으로 연관관계의 주인을 지정해야 함
  • 연관관계의 주인은 외래 키가 있는 곳으로 정해야 함 (여기서는 Member.team)

 

 

양방향 연관관계 저장

public void testSave() {

    //팀1 저장
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);

    //회원1 저장
    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1);
    em.persist(member1);

    //회원2 저장
    Member member2 = new Member("member2", "회원2");
    member2.setTeam(team1);
    em.persist(member2);
}

 

객체 관점에서는 양쪽 방향에 모두 값을 입력해주는 것이 안전

public void testSave() {

    //팀1
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);
    
    Member member1 = new Member("member1", "회원1");
    
    //양방향 연관관계 설정
    member1.setTeam(team1);
    team1.getMembers().add(member1);
    em.persist(member1);
    
    Member member2 = new Member("member2", "회원2");    
    
    //양방향 연관관계 설정
    member2.setTeam(team1);
    team1.getMembers().add(member2);
    em.persist(member2);
}

 

양방향 관계에서 두 코드는 하나인 것처럼 사용하는 것이 안전

public class Member {

    private Team team;

    public void setTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);
    }
}

 

연관관계 변경 시에는 기존 관계를 제거하고 변경해야 한다.

public void setTeam(Team team) {

    //기존 팀과 관계를 제거
    if (this.team != null) {
    	this.team.getMembers().remove(this);
    }
    this.team = team;
    team.getMembers().add(this);
}