객체는 참조를 통해 관계를 맺고, 테이블은 외래 키를 사용해서 관계를 맺는다.
이 패러다임의 불일치를 매핑시키는 것을 신경 써야 한다.
Member
와 Team
은 단방향 관계이다.Member
객체는 Member.team
필드를 통해서 팀 객체와 연관관계를 맺는다.Member
는 Team
필드를 통해서 참조가 가능하지만, Team
필드는 Member
필드 참조가 불가능TEAM_ID
외래 키를 통해서 연관관계를 맺는다.Member join Team
, Team join Member
두 개가 모두 가능하다.public class Member {
private String id;
private String username;
private Team team;
public void setTeam(Team team) {
this.team = team;
}
}
public class Team {
private String id;
private String name;
}
public static void main(String[] args) {
Member member1 = new Member("member1", "회원1");
Member member2 = new Member("member2", "회원2");
Team team1 = new Team("team1", "팀1");
member1.setTeam(team1);
member2.setTeam(team1);
Team findTeam = member1.getTeam();
}
CREATE TABLE MEMBER (
MEMBER_ID VARCHAR(255) NOT NULL,
TEAM_ID VARCHAR(255),
USERNAME VARCHAR(255),
PRIMARY KEY (MEMBER_ID)
)
CREATE TABLE TEAM (
TEAM_ID VARCHAR(255) NOT NULL,
NAME VARCHAR(255),
PRIMARY KEY (TEAM_ID)
)
ALTER TABLE MEMBER ADD CONSTRAINT FK_MEMBER_TEAM
FOREIGN KEY (TEAM_ID)
REFERENCES TEAM
INSERT INTO TEAM(TEAM_ID, NAME) VALUES('team1', '팀1');
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME)
VALUES('member1', 'team1', '회원1');
INSERT INTO TEAM(TEAM_ID, NAME) VALUES('team1', '팀1');
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME)
VALUES('member2', 'team1', '회원2');
SELECT T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
WHERE M.MEMBER_ID = 'member1';
@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;
}
}
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
}
@ManyToOne
@JoinColumn(name = “TEAM_ID”)
name
속성에는 매핑할 외래 키 이름으로 지정한다.//팀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");
member2.setTeam(team1);
em.persist(member2);
Member member = em.find(Member.class, "member1");
Team team = member.getTeam();
:teamName
: :
로 시작하는 것은 파라미터를 바인딩받는 문법이다.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(member.getUsername()); // 회원1, 회원2 출력
}
UPDATE
쿼리가 날라간다.Team team2 = new Team("team2", "팀2");
em.persist(team2);
Member member = em.find(Member.class, "member1");
member.setTeam(team2);
Member member1 = em.find(Member.class, "member1");
member1.setTeam(null);
member1.setTeam(null); //연관관계 제거
member2.setTeam(null); //연관관계 제거
em.remove(team); //팀 삭제
회원 → 팀 (Member.team) ,다대일 관계
팀 → 회원 (Team.members) , 일대다 관계, 컬렉션(List) 사용
데이터베이스는 외래키 하나로 양방향 조회가 가능하므로 수정할 필요가 없다.
mappedBy
//회원 엔티티는 그대로
//팀 엔티티
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
Team team = em.find(Team.class, "team1");
List<Member> members = team.getMembers();
엄밀히 따지면 객체는 양방향 연관관계가 아닌, 서로 다른 2개의 단뱡향 연관관계이다.
하지만, 테이블은 하나의 외래키로 양방향 연관관계를 관리한다.
엔티티를 양방향 연관관계로 설정하면, 객체의 참조는 2개인데, 외래키는 1개인 차이가 발생한다.
JPA는 이러한 문제를 해결하기 위해, 테이블 하나를 정해서 연관관계를 관리하는 연관관계의 주인으로 설정한다.
mappedBy
mappedBy
속성을 사용하지 않는다.mappedBy
속성을 통해 속성 값으로 연관관계의 주인을 가리킨다.Member.team
을 주인으로 설정하면 자기 테이블에 있는 외래 키를 관리하면 된다.Team.members
를 주인으로 설정하면 회원 테이블에 있는 외래 키를 관리해야 한다.Member.team
을 주인으로 설정한다.Team.members
에는 mappedBy
를 통해 연관관계의 주인을 가리키도록 설정한다.team1.getMembers().add(member1)
//팀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");
member2.setTeam(team1);
em.persist(member2);
TEAM_ID
필드에 null
값으로 채워진다.
//멤버1 저장
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
em.persist(member1);
//멤버2 저장
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1);
em.persist(member2);
Team team = new Team("team1", "팀1");
//주인이 아닌 곳만 연관관계 설정
team1.getMembers().add(member1);
team1.getMembers().add(member2);
//팀1
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
team1.getMembers.add(member1);
em.persist(member1);
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1);
team1.getMembers.add(member2);
em.persist(member2);
setTeam()
메서드 하나로 양방향 관계를 모두 설정할 수 있다.public class Member {
private Team team;
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
//팀1
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
em.persist(member1);
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1);
em.persist(member2);
member1.setTeam(teamA);
member1.setTeam(teamB);
Member findMember = teamA.getMember(); // member1이 여전히 조회된다.
//기존 관계 제거
public void setTeam (Team team) {
//기존 팀과 관계를 제거
if (this.team != null) {
this.team.getMembers().remove(this);
}
this.team = team;
team.getMembers.add(this);
}
teamA
의 getMembers()
를 호출 시 member1
이 여전히 반환될 수 있으므로, 관계를 제거하는 것이 안전하다.