
출처 : 인프런 > 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 듣고 작성한 글입니다.
강의 링크 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
‘객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.’
– 조영호(객체지향의 사실과 오해)
예제 시나리오

@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId;
...
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
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());
// 연관관계가 없음
Long findTeamId = findMember.getTeamId();
Team findTeam = em.find(Team.class, findTeamId);
객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.
- 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
- 객체는 참조를 사용해서 연관된 객체를 찾는다.
- 테이블과 객체 사이에는 이런 큰 간격이 있다.

team의 id가 아니라 참조값을 그대로 가져옴
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
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_ID(PK)를 연관관계 매핑
/* 연관관계 저장 */
// 팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
// 회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); // 단방향 연관관계 설정, 참조 저장
// (jpa가 알아서 team에서 pk값을 꺼내서 fk값에 인서트)
em.persist(member);
em.flush();
em.clear();
/* 참조로 연관관계 조회 - 객체 그래프 탐색 */
// 조회
Member findMember = em.find(Member.class, member.getId());
// 참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
/* 연관관계 수정 */
// 새로운 TeamB
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 TeamB 설정
member.setTeam(teamB);

@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
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;
...
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
...
}
// 조회
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
for (Member m : members) {
System.out.println("m = " + m.getName());
}
객체 연관관계 = 2개
- 회원 -> 팀 연관관계 1개(단방향)
- 팀 -> 회원 연관관계 1개(단방향)
테이블 연관관계 = 1개
- 회원 <-> 팀의 연관관계 1개(양방향)

// A -> B (a.getB())
class A {
B b;
}
// B -> A (b.getA())
class B {
A a;
}
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID

양방향 매핑 규칙
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)
- 📢 주인이 아닌쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용X
- 주인이 아니면 mappedBy 속성으로 주인 지정

(연관관계의 주인에 값을 입력하지 않음)
// 회원 저장
Member member = new Member();
member.setName("member1");
em.persist(member);
// 팀 저장
Team team = new Team();
team.setName("TeamA");
// 역방향(주인이 아닌 방향)만 연관관계 설정
// 연관관계 주인은 Member에 있는 Team
// team에 있는 members는 mappedBy여서 읽기 전용
// JPA에서 업데이트나 인서트할 때 members를 보지 않음
team.getMembers().add(member);
em.persist(team);

(순수한 객체 관계를 고려하면 항상 양쪽다 값을 입력해야 한다.)
// 팀 저장
Team team = new Team();
team.setName("TeamA");
// team.getMembers().add(member);
em.persist(team);
// 회원 저장
Member member = new Member();
member.setName("member1");
//연관관계의 주인에 값 설정
member.setTeam(team);
em.persist(member);


