JPA - (다대일) 단방향 연관관계

이유석·2023년 1월 5일
1

JPA - Entity

목록 보기
4/14
post-thumbnail

단방향 연관관계의 이해를 위해, 다대일(N:1) 단방향 관계로 설명을 해보겠습니다.

객체 및 테이블 모델링

이해를 돕기 위해, 회원(Member)과 팀(Team)의 관계를 예시로 들어보겠습니다.

  • 회원과 팀이 있다.
  • 회원은 하나의 팀에만 소속될 수 있다.
  • 다수의 회원은 하나의 팀에 소속될 수 있습니다.
  • 즉, 회원관 팀은 다대일(N:1)의 관계입니다.

위 조건에 다대일 단방향 관계를 위한 추가 조건은 아래와 같습니다.

  • 회원 객체와 팀 객체는 단방향 관계입니다.
  • 회원 객체(Member)는 Member.team 필드를 통해서 회원이 속한 팀 객체(Team)에 접근할 수 있습니다.
  • 팀 객체(Team)는 팀에 속한 회원 객체(Member)에 접근할 수 없습니다.

위 관계를 통해서 객체 및 테이블 모델링을 한 결과는 아래와 같습니다.

객체 관계 매핑

해당 객체 모델링을 코드로 나타내어 보도록 하겠습니다.

코드 설명

Member 클래스 (다대일에서 에 해당합니다.)

@Entity
public class Member {
   @Id
   @Column(name = "MEMBER_ID)
   private Long id;
 
   @Column(name = "USERNAME")
   private String username;
 
   @ManyToOne
   @JoinColumn(name = "TEAM_ID")
   private Team team;
  
  // Getter, Setter, Constructor...
}

Team 클래스 (다대일에서 에 해당합니다.)

@Entity
public class Team {
	@Id
    @Column(name = "TEAM_ID)
    private Long id;
    
    @Column(name = "NAME")
    private String name;
    
    // Getter, Setter, Constructor
}

다대일 단방향 매핑에서는 에 해당하는 클래스에 에 해당하는 클래스를 참조 필드로 작성해주시면 됩니다.
이때, 해당 참조 필드위에 @ManyToOne@JoinColumn(name = "외래키 이름")을 추가하여 줍니다.

@JoinColumn

외래키를 매핑할 때 사용합니다.

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

JoinColumn 어노테이션은 생략 가능합니다.
@JoinColumn을 생략하면 외래 키를 찾을 때 기본 전략을 사용합니다.
기본 전략 : 필드명 + _ + 참조하는 테이블의 컬럼명

@ManyToOne

다대일 관계에서 사용합니다.

속성기능기본값
optionalfalse로 설정하면 연관된 엔티티가 항상 있어야 한다true
fetch글로벌 패치 전략을 설정한다.
(자세한 내용은 추후에)
@ManyToOne=FetchType.EAGER (즉시 로딩), @OneToMany=FetchType.LAZY (지연 로딩)
cascade영속성 전이 기능을 사용한다.
(자세한 내용은 추후에)
targetEntity연관된 엔티티의 타입 정보를 설정한다.
이 기능은 거의 사용하지 않음

연관관계 사용

연관관계를 등록, 수정, 삭제, 조회하는 예제를 통해 연관관계를 어떻게 사용하는지 알아보겠습니다.

저장

public void teamSave() {
	
    // 팀 1 저장
    Team team1 = new Team(0L, "팀1");
    entityManager.persist(team1); // 영속화
    
    // 회원 1 저장
    Member member1 = new Member(0L, "회원1");
	member1.setTeam(team1); // 연관관계 설정 member1 → team1
	entityManager.persist(member1);
    
    // 회원 2 저장
    Member member2 = new Member(1L, "회원2");
    member2.setTeam(team1); // 연관관계 설정 member2 → team1
    entityManager.persist(member2);
}
  • JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태이어야 합니다.

  • 위 코드를 실행하였을 때, 실행되는 SQL 은 아래와 같습니다.

INSERT INTO TEAM (TEAM_ID, NAME) VALUES (0, '팀1');
INSERT INTO MEMBER (MEMBER_ID, USERNAME, TEAM_ID) VALUES (0, '회원1', 0);
INSERT INTO MEMBER (MEMBER_ID, USERNAME, TEAM_ID) VALUES (1, '회원2', 0);

조회

연관관계가 있는 엔티티를 조회하는 방법은 크게 2가지가 있습니다.

객체 그래프 탐색
member.getTeam() 을 사용해서 member와 연관된 team 엔티티를 조회할 수 있습니다.

Member member = entityManager.find(Member.class, 0L);
Team team = member.getTeam(); // 객체 그래프 탐색
System.out.println("팀 이름 = " + team.getName());

// 출력 결과 : 팀 이름 = 팀1
  • 위 코드를 실행하였을 때, 실행되는 SQL 문은 아래와 같습니다.
SELECT m.MEMBER_ID, m.TEAM_ID, m.USERNAME, t.TEAM_ID, t.NAME
FROM 
	Member m
	LEFT OUTER JOIN
	Team t
	ON m.TEAM_ID = t.TEAM_ID
WHERE m.MEMBER_ID = 0;

객체지향 쿼리 (JPQL) 사용

String jpql = "select m.team from Member m where m.id = :memberId";

Team team = entityManager.createQuery(jpql, Team.class)
                .setParameter("memberId", 0L)
                .getSingleResult();
System.out.println("팀 이름 = " + team.getName());

// 출력 결과 : 팀 이름 = 팀1
  • 위 코드를 실행하였을 때, 실행되는 SQL 은 아래와 같습니다.
SELECT t.*
FROM Team t
	JOIN Member m ON t.TEAM_ID = m.TEAM_ID
WHERE m.MEMBER_ID = 0;

수정

팀1 소속이던 회원1 을 새로운 팀2 에 소속하도록 수정해보겠습니다.

// 새로운 팀2 영속화
Team team2 = new Team(1L, "팀2");
entityManager.persist(team2);

// 회원1에 새로운 팀2 설정
Member member = entityManager.find(Member.class, 0L);
member.setTeam(team2);

수정은 entityManager.update(); 와 같은 메서드가 없으며,
조회한 엔티티의 값만 변경해두면 트랜잭션을 커밋할 때, 플러시가 일어나면서 변경 감지 기능이 작동합니다.
이때, 변경사항을 데이터베이스에 자동으로 반영해줍니다.

  • 위 코드를 실행하였을 때, 실행되는 SQL 은 아래와 같습니다.
UPDATE MEMBER
SET
	TEAM_ID = 1, ...
WHERE
	MEMBER_ID = 0;

연관관계 제거

회원1 을 어느 팀에도 소속하지 않도록 변경해보겠습니다.

Member member1 = entityManager.find(Member.class, 0L);
member1.setTeam(null);

위 코드를 실행했을 때, 실행되는 SQL 은 아래와 같습니다.

UPDATE MEMBER
SET
	TEAM_ID = null, ...
WHERE
	MEMBER_ID = 0;

삭제

다대일 연관관계에서 에 연관된 엔티티인 Team 객체들 중, 팀1 을 제거해보겠습니다.

이때, 기존에 있던 연관관계를 먼저 제거하고 삭제를 수행해야 합니다.
그렇지 않으면 외래 키 제약 조건에 의해 데이터베이스에서 오류가 발생합니다.

// 나머지 회원2 에 대한 연관관계를 제거한다.
Member member2 = entityManager.find(Member.class, 1L);
member2.setTeam(null);

Member team = entityManager.find(Team.class, 0L);
entityManager.remove(team);
DELETE 
FROM TEAM
WHERE TEAM_ID = 0;

소스코드

profile
소통을 중요하게 여기며, 정보의 공유를 통해 완전한 학습을 이루어 냅니다.

1개의 댓글

comment-user-thumbnail
6일 전

When visiting the capital, choose Escorts Daryaganj for thrilling adventures filled with romance and relaxation. These escorts understand how to pamper clients with care and affection. They’re trained in offering high-class companionship, whether it's for travel, parties, or intimate moments.

답글 달기