개발 도서 읽기/자바 ORM 표준 JPA 프로그래밍

[5장]5.1~5.2 단방향 연관관계

nnhhmm 2024. 11. 11. 14:39

엔티티들은 대부분 다른 엔티티와 연관관계가 있다. 

객체의 참조와 테이블의 외래 키를 매핑하는 것이 이 장의 목표다.

키워드 
방향(Direction) 단방향: 회원 -> 팀 또는 팀 -> 회원 둘 중 한 쪽만 참조하는 것을 단방향 관계라한다.
양방향: 회원 -> 팀, 팀 -> 회원 양 쪽 모두 서로 참조하는 것을 양방향 관계라 한다.
방향은 객체관계에만 존재하고 테이블 관계는 항상 양방향이다.
다중성(Multiplicity) 다대일(N:1) : 여러 회원은 한 팀에 속하므도 회원과 팀은 다대일 관계
일대다(1:N) : 한 팀에 여러 회원이 소속될 수 있으므로 팀과 회원은 일대다 관계
일대일(1:1)
다대다(N:N)
연관관계의 주인(owner) 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 한다. 

 

5.1 단방향 연관관계 

  • 객체 연관관계
    회원 객체는 Member.team 필드(멤버변수)로 팀 객체와 연관관계를 맺는다
    회원 객체와 팀 객체는 단방향 관계다. 회원은 Member.team 필드를 통해서 팀을 알수있지만 반대로 팀은 회원을 알 수 없다.
  • 테이블 연관관계
    회원 테이블은 TEAM_ID 외래 키로 팀 테이블과 연관관계를 맺는다.
    회원 테이블과 팀 테이블은 양방향 관계다. 회원 테이블의 TEAM_ID 외래 키를 통해서 회원과 팀을 조인할 수 있고 반대로 팀과 회원원도 조인할 수있다.
  • 객체는 참조(주소)로 연관관계를 맺는다.
  • 테이블은 외래 키로 연관관계를 맺는다.

객체 관계 매핑 

@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 team){
    	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 외래 키 컬럼을 사용

  • Member.team과 MEMBER_ID를 매핑하는 것이 연관관계 매핑이다.
  • 연관관계 매핑 코드
    @ManyToOne
    @JoinColumn(name="TEAM_ID")
    private Team team;
  • @ManyToOne : 이름 그대로 다대일(N:1) 관계라는 매핑 정보다
    연관관계를 매핑할 때 이렇게 다중성을 나타내는 어노테이션을 필수로 사용해야 한다.
  • @JoinColumn(name="TEAM_ID"): 조인 컬럼은 외래 키를 매핑할 때 사용한다.
    name속성에는 매핑할 외래 키 이름을 지정한다. 이 어노테이션은 생략할 수 있다.

@JoinColumn

속성 기능 기본값
name 매핑할 외래 키 이름 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명
referencedColumnName 외래 키가 참조하는 대상 테이블의 컬럼명 참조하는 테이블의 기본키 컬럼명
foreignKey 외래 키 제약조건을 직접 지정할 수 있다.
이 속성은 테이블을 생성할 때만 사용한다.
 
unique
nuallable
insertable
updatable
columnDefinition
table
@Column의 속성과 같다.  

 

@ManyToOne

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

 

5.2 연관 관계 사용

1)  저장

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");
    member1.setTeam(team1);
    em.persist(member2);
}

 

member1.setTeam(team1); // 회원 -> 팀 참조

em.persist(member1); // 저장

 

2) 조회

  • 객체 그래프 탐색
    member.getTeam()을 사용해서 member와 연관된 Team 엔티티를 조회할 수 있다.
Member member = em.find(Member.class, "member1");
Team team = member.getTeam() ; // 객체 그래프 탐색
System.out.println("팀 이름 = "+ team.getName());

// 출력결과 : 팀 이름 = 팀1

 

  • 객체지향 쿼리 (JPQL) 사용
public static void queryLogicJoin(EntityManager em){

	String jpql = "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) {
    	System.out.println("[query] memer.username=" + member.getUsername());
    }
}

// 결과 : [query] member.username=회원1
// 결과 : [query] member.username=회원2

 

 

3) 수정

public static void updateRelation(EntityManager em){
	
    // 새로운 팀2
    Team team2 = new Team("team2" ,"팀2");
    em.persist(team2);
    
    // 회원1에 새로운 팀2 설정
    Member member = em.find(Member.class, "member1");
    member.setTeam(team2);
}

 

단순히 불러온 엔티티의 값만 변경해두면 트랜잭션을 커밋할 때 플러시가 일어나면서 변경 감지 기능이 작동한다. 그리고 변경사항을 데이터 베이스에 자동으로 반영한다. 

 

4) 연관관계 제거 

public static void deleteRelation(EntityManager em){
   
    Member member1 = em.find(Member.class, "member1");
    member1.setTeam(null); // 연관관계 제거 
}

 

5) 연관된 엔티티 삭제

member1.setTeam(null); // 회원1 연관관계 제거

member2.setTeam(null); // 회원2 연관관계 제거

em.remove(team);           // 팀 삭제