새소식

back/spring

Orm 표준 jpa 프로그래밍 (3) - 연관관계 매핑

  • -

 

ex1. 객체를 테이블에 맞추어 모델링

// (참조 대신에 외래키를 그대로 사용)
@Entity

public class Member {


 	@Id @GeneratedValue

    private Long id;
 

    
	@Column(name = "USERNAME")
 
    private String name;

    
    @Column(name = "TEAM_ID")

	private Long teamId; 
    ...
}

@Entity

public class Team {

	@Id @GeneratedValue

    private Long id;

    private String name;
    ...
}


// (외래 키 식별자를 직접 다룸)
//팀 저장

Team team = new Team();

team.setName("TeamA");
 em.persist(team);



//회원 저장

Member member = new Member();

member.setName("member1");

member.setTeamId(team.getId());

em.persist(member);


// (식별자로 다시 조회, 객체 지향적인 방법은 아니다.)
//조회

Member findMember = em.find(Member.class, member.getId());
//연관관계가 없음

Team findTeam = em.find(Team.class, team.getId());

- 객체를 테이블에 맞추어 데이터 중심으로 모델링하면 협력관계를 만들 수 없다.

> 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾음

> 객체는 참조를 사용해서 연관된 객체를 찾음

> 테이블과 객체 사이에는 이러한 간격이 있음

 

 

1. 단방향 연관관계

@Entity

public class Member {


	@Id @GeneratedValue

    private Long id;

    
    @Column(name = "USERNAME")

    private String name;

    private int age;

    
    //@Column(name = "TEAM_ID")

    //private Long teamId;

	@ManyToOne

	@JoinColumn(name = "TEAM_ID")

	private Team team;

    ...



// 연관관계 저장
//팀 저장

Team team = new Team();

team.setName("TeamA");

em.persist(team);



//회원 저장

Member member = new Member();

member.setName("member1");

member.setTeam(team); //단방향 연관관계 설정, 참조 저장

em.persist(member);


//조회

Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회

Team findTeam = findMember.getTeam();


// 새로운 팀B

Team teamB = new Team();

teamB.setName("TeamB");

em.persist(teamB);



// 회원1에 새로운 팀B 설정

member.setTeam(teamB);

 

 

2. 양방향 연관관계와 연관관계의 주인

 

// (Member 엔티티는 단방향과 동일)
@Entity

public class Member {

	@Id @GeneratedValue
	private Long id;
 

    
	@Column(name = "USERNAME")

    private String name;
 
    private int age;



	@ManyToOne

	@JoinColumn(name = "TEAM_ID")

	private Team team;

    ...


// (Team 엔티티는 컬렉션 추가)
@Entity

public class Team {

	@Id @GeneratedValue

    private Long id;




	private String name;


	@OneToMany(mappedBy = "team")

	List<Member> members = new ArrayList<Member>();
    ...
}

//조회

Team findTeam = em.find(Team.class, team.getId());

int memberSize = findTeam.getMembers().size(); //역방향 조회

 

 

객체의 양방향 관계

- 서로 다른 단방향 관계가 2개인 것

- 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 함

 

테이블의 양방향 관계

- 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리

 

연관관계의 주인

- 객체의 두 관계중 하나를 연관관계의 주인으로 지정

 - 연관관계의 주인만이 외래 키를 관리(등록,수정)

- 주인이 아닌쪽은 읽기만 가능 (외래키에 영향을 주지 않고 단순 조회만 가능)

- 주인은 mappedBy 속성 사용 X

- 주인이 아니면 mappedBy 속성으로 주인을 지정

- 주인은 외래키가 있는곳이 되어야 함

 

 

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

em.persist(team);



Member member = new Member();
member.setName("member1");





//역방향(주인이 아닌 방향)만 연관관계 설정

team.getMembers().add(member);

//양방향 매핑시 가장 많이 하는 실수: 연관관계의 주인에 값을 입력하지 않음

//실수 해결 >> 연관관계의 주인에 값 설정
// 양방향 매핑시 연관관계의 주인에 값을 입력해야 함
//순수한 객체 관계를 고려하면 항상 양쪽다 값을 입력해야 함
member.setTeam(team); //**

em.persist(member);

-  양방향 연관관계는 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정해야 함

- 연관관계 편의 메소드 추천

- 양방향 매핑시에는 무한루프 조심해야 함!! ( toString(), lombok, JSON 생성 라이브러리 )

 

 

- 단방향 매핑만으로도 이미 연관관계 매핑은 완료된것이다.

- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색)기능이 추가된 것 뿐

- JPQL에선 역방향으로 탐색할 일이 많음

- 단방향 매핑을 하고 필요할 때 양방향 매핑을 추가하는 것이 좋음 (테이블에 영향 X)

 

 

3. 다대일

 

3-1. 다대일 단방향

- 가장 많이 사용하는 연관관계

 

3-2. 다대일 양방향

- 외래 키가 있는 쪽이 연관관계의 주인

- 양쪽을 서로 참조하도록 개발

 

4. 일대다

 

4-1. 일대다 단방향

- 1이 연관관계의 주인

- 테이블 일대다 관계는 항상 '다(n)'쪽에 외래 키가 있음

- 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하는 특이한 구조

일대다 단방향 매핑보다는 다대일 양방향 매핑 사용

 

4-2. 일대다 양방향

- 공식적으로 존재 X

- 다대일 양방향 사용하기

 

5. 일대일

 

- 주 테이블이나 대상 테이블 중에 외래 키 선택 가능

- 외래 키에 DB 유니크 제약 조건 추가

 

5-1. 주 테이블에 외래 키 단방향

- 다대일 단방향 매핑과 유사

 

5-2. 주 테이블에 외래 키 양방향

- 다대일 양방향 매핑 처럼 외래 키가 있는 곳이 연관관계의 주인, 반대편은 mappedBy 적용

 

5-3. 대상 테이블에 외래 키

- 단방향은 jpa가 지원하지 않음, 양방향은 지원

-  일대일 주 테이블에 외래 키 양방향과 매핑 방법은 같음

 

>> 주 테이블에 외래 키

- 객체지향 개발자가 선호하며 jpa 매핑이 편리

- 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능

- 단점: 값이 없으면 외래 키에 null 허용됨

 

>> 대상 테이블에 외래 키

- 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조가 유지됨

- 단점: 프록시 기능의 한계로 지연 로딩 설정을 해도 즉시 로딩이 됨

 

 

6. 다대다

- RDBMS는 정규화된 테이블 2개로 다대다 관계 표현 불가능 => '연결 테이블'을 추가해서 일대다, 다대일 관계로 풀어내야 함

 

- 객체는 '컬렉션'을 사용해서 객체 2개로 다대다 관계 가능

- @ManyToMany 사용하며, @JoinTable로 연결 테이블을 지정

- 다대다 매핑은 단방향/양방향 둘 다 가능

- 편리해 보이지만 실무에서는 사용 X (연결 테이블이 단순히 연결만 하지 않고, 추가 데이터가 더 들어올 수 있음)

 

- 다대다의 한계를 극복하기 위해 연결 테이블용 엔티티를 추가 할 수 있음(연결 테이블을 엔티티로)

- @ManyToMany >> @OneToMany + @ManyToOne

 

 

 

@JoinColumn 의 주요 속성

1. name: 매핑할 외래 키 이름 (기본값은 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명)

2. referencedColumnName: 외래 키가 참조하는 대상 테이블의 컬럼명 (기본값: 참조하는 테이블의 기본키 컬럼명)

3. foreignKey(DDL): 외래 키 제약 조건을 직접 지정, 테이블 생성 시에만 사용

 

 

@ManyToOne 의 주요 속성

1. Optional: false로 설정하면 연관된 엔티티가 항상 있어야 한다(기본값: true)

2. fetch (기본값: @ManyToOne=FetchType.EAGER / @OneToMany=FetchType.LAZY)

3. cascade: 영속성 전이 기능을 사용

 

 

@OneToMany 의 주요 속성

1. mappedBy: 연관관계의 주인 필드를 설정 

( '다(n)' 쪽에 외래키가 있고 외래 키가 있는 쪽이 연관관계의 주인이므로 '일(one)'쪽에서 mappedBy로 주인 필드 지정함)

2. fetch (기본값: @ManyToOne=FetchType.EAGER / @OneToMany=FetchType.LAZY)

3. cascade: 영속성 전이 기능을 사용

 

 

 

 

 

Contents

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

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