지난 포스팅에서는 JPA의 Entity에 대해서 작성을 했었는데요.
Entity란 JPA에서 관리하는 DB와 매핑이 되는 객체를 뜻하며 객체 클래스 위에 @Entity을 사용해서 JPA에게 '해당 객체는 Entity로 사용을 할 것이다' 라고 명시를 했습니다.
그렇다면 이번 포스팅에서는 'JPA에서는 DB의 연관 관계를 어떻게 사용을 하는 것인가?', '두개의 테이블을 join을 어떻게 객체화 시키지?' 라는 주제로 Entity의 연관 관계에 대해서 알아보도록 하겠습니다.
RDB Table의 연관관계
프로젝트에서 사용하는 DB는 Mysql, MariaDB, Oracle 등 RDB (관계형 데이터 베이스)를 사용을 하는데 이러한 RDB에서는 FK (Foreign Key)를 사용해서 두 테이블간의 연관 관계를 정의 할 수 있다.
team과 member라는 두개의 테이블이 존재한다고 가정을 했을 때 하나의 팀은 여러명의 멤버를 수용할 수 있고 한명의 멤버는 하나의 팀만 들어갈 수 있는 구조로 team 테이블과 member 테이블은 1:N 구조라고 볼 수 있다.
한명의 멤버가 어떤 팀에 속해 있는지는 member 테이블의 team_seq라는 컬럼을 보면 알 수 있는데 이는 team 테이블의 PK값과 연관 되어있고 보면 된다.
Entity 연관관계
- RDB에서는 하나의 FK를 가지고 양쪽 테이블의 데이터를 모두 조회 할 수 있지만 Entity의 경우 참조용 필드가 존재하는 쪽만 연관 관계를 가질 수 있다.
- 흔히 단방향, 양방향 연관관계 라고 설명을 하는데 두 개의 엔티티에서 한쪽만 참조를 할 경우 단방향, 양쪽 모두 각각 단방향 참조를 할 경우 양방향 연관 관계라고 할 수 있다.
- @OneToMany, @ManyToOne 등 어노테이션을 통해서 Entity 객체를 참조하여 연관 관계를 정의할 수 있다.
단방향 연관관계 - @ManyToOne
- RDB 관계에서 1:N 구조의 테이블에서 N에 해당되는 자식 테이블과 매핑되는 엔티티 객체에서 사용
- @JoinColumn 어노테이션을 사용해 해당 테이블의 FK 컬럼을 명시한다.
- 필드는 자료형 대신 부모 테이블과 매핑되는 엔티티 객체를 참조형 필드로 선언한다.
양방향 연관관계 - @OneToMany
- RDB 관계에서 1:N 구조의 테이블에서 1에 해당되는 부모 테이블과 매핑되는 엔티티 객체에서 사용
- mappedBy 옵션을 통해 참조 객체의 필드명을 명시한다.
- 1:N의 구조에서 N에 해당되는 데이터를 가져와야 하니 List 자료형을 사용하며 제네릭으로 매핑 관계에 있는 엔티티 객체를 참조한다.
@Entity
public class Team {
@Id
@GeneratedValue
private Long seq;
private String name;
@OneToMany(mappedBy="team")
private List<Member> memberList = new ArrayList<Member>();
}
@Entity
public class Member {
@Id
@GeneratedValue
private Long seq;
private String name;
private int age;
@ManyToOne
@JoinColumn(name="team_seq")// member 테이블의 team_seq 컬럼 명시
private Team team; // 부모 테이블 매핑 객체인 Team 엔티티 참조
}
DB와 JPA 연관관계 차이
- RDB에서 테이블의 연관관계는 FK (Foreign Key) 하나로 두 테이블 사이의 데이터를 출력 할 수 있다.
- JPA에서 두 객체의 연관관계는 양방향성 연관 관계가 잇다고 하지만 각각 객체에서 다른 객체를 참조하는 단방향 관계이다.
- A 엔티티에서 B를 참조하고 B 엔티티에서 A를 참조를 하는 방식, 2개의 단방향성 연관관계가 존재
양방향 연관관계의 주인
- 서로 참조를 하고 있는 양방향성 연관관계에서 JPA에서 연관관계 주인을 정해줘야 한다.
- 연관관계의 주인만이 FK (Foreign Key)를 관리 한다.
- mappedBy 속성을 사용하지 않는 쪽이 연관관계의 주인이 된다.
- DB Table에서 FK가 있는 테이블의 객체쪽이 연관관계의 주인이 된다.
- 연관관계의 주인이 아닌 쪽은 읽기만 가능하다.
public class TestService {
public void entityTest(){
// 엔티티 매니저 객체 생성
EntityManager em = emf.createEntityManager();
// 트랜잭션 시작
em.getTransaction().begin();
Team team = new Team();
team.setName("team1");
em.persist(team);
Member member = new Member();
member.setName("member1");
em.persist(team);
team.getMemberList().add(member);
em.getTransaction().commit();
}
}
위 코드에서 17줄 team.getMembers().add(member); 에서 Team 객체 안에 참조 되어있는 Member List에 신규 멤버인 member를 추가를 하고 commit을 하게 되면 DB Table에는 아무런 변화가 일어나지 않는다.
즉 member 테이블의 team_seq 의 데이터는 null로 표시가 될 것이다.
이와 같은 이유는 JPA에서 연관관계의 주인만이 해당 연관관계에 있는 데이터를 등록 및 수정, 삭제를 진행할 수 있고 주인이 아닌 관계에서는 읽기 전용으로 데이터를 가져올 수 밖에 없다.
public class TestService {
public void entityTest(){
// 엔티티 매니저 객체 생성
EntityManager em = emf.createEntityManager();
// 트랜잭션 시작
em.getTransaction().begin();
Team team = new Team();
team.setName("team1");
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(team);
em.getTransaction().commit();
}
}
위 코드와 같이 15줄에 member 객체 안에 참조를 시켜둔 Team 필드를 setter를 통해 데이터를 세팅하고 commit을 하게 된다면 member 테이블의 team_seq 데이터가 들어가는 모습을 볼 수 있을 것이다.
연관관계 사용 시 주의사항에 대해 포스팅을 하면 글이 너무 길어져서 지루해질 수 있기 때문에 연관관계 사용 시 주의사항은 다음 포스팅에서 더 자세히 작성을 하도록 하겠습니다.
감사합니다:)
'Spring' 카테고리의 다른 글
Spring JPA / org.hibernate.LazyInitializationException: could not initialize proxy 장애 극복기 (0) | 2023.06.29 |
---|---|
Spring JPA / 양방향 연관관계 주의사항 (0) | 2023.06.27 |
Spring JPA / Entity 매핑 (0) | 2023.06.21 |
Spring JPA / JPA (0) | 2023.06.20 |
Spring AOP (0) | 2023.06.13 |