객체 연관관계와 테이블 연관관계의 가장 큰 차이
객체 연관관계 vs 테이블 연관관계 정리
순수한 객체 연관관계
public class Member {
	private String id;
	private String username;
	
	private Team team // 팀의 참조 보관
	
	//Getter, Setter ..
}
public class Team {
	private String id;
	private String name;
	//Getter , Setter ..
}
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();
}테이블 연관관계
객체 관계 매핑
@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;
	}
	//Getter, Setter ..
}
@Entity
public class Team {
	@Id
	@Column(name = "TEAM_ID")
	private String id;
	private String name;
	//Getter, Setter ..
}@ManyToOne
@JoinColumn(name="TEAM_ID")
저장
//JPA 에서 엔티티를 저장 할때 연관된 모든 엔티티는 영속 상태여야 한다!
public void testSave() {
	
	//팀1 저장
	Team team1 = new Team("team1", "팀1");
	em.persist(team1);
	//회원1 저장
	Member member1 = new Member("member1", "회원1");
	member1.setTeam(team1); //연관관계 설정 member1 -> team1
	em.persist(member1);
	
	//회원2 저장
	Member member2 = new Member("member2", "회원2");
	member2.setTeam(team1); //연관관계 설정 member2 -> team1
	em.persist(member2);
}
//결과
INSERT INTO TEAM (TEAM_ID, NAME) VALUES ('team1' , '팀1')
INSERT INTO MEMBER (MEMBER_ID, NAME, TEAM_ID) VALUES('member1', '회원1','team1')
INSERT INTO MEMBER (MEMBER_ID, NAME, TEAM_ID) VALUES('member2', '회원2','team1')조회
객체 그래프 탐색 (객체 연관관계를 사용한 조회)
객체지향 쿼리 사용 (JPQL)
private static void queryLogicJoin(EntityManager em) {
	String jpq1 = "select m from Member m join m.team t where " +
								"t.name=:teamName"; //:으로 시작-> 파라미터 바인딩 받는 문법
	List<Member> resultList = em.createQuery(jpq1, Member.class)
			.setParameter("teamName","팀1")
			.getResultList();
	for (Member m : resultList) {
		System.out.println("[query] m.username=" +
			m.getUsername());
	}
		
}
//결과 : [query] m.username=회원1
//결과 : [query] m.username=회원2수정
private static void updateRelation(EntityManager em) {
	//새로운 팀2
	Team team2 = new Team("team2", "팀2");
	em.persist(team2);
	//회원1에 새로운 팀2 설정
	Member member = em.find(Member.class , "member1");
	member.setTeam(team2);
}
//실행되는 수정 SQL
UPDATE MEMBER
SET
	TEAM_ID='team2', ...
WHERE
	ID='member1'
//트랜잭션 커밋시 플러시가 일어나면서 변경 감지 기능 작동!
//참조 대상만 변경하면 나머지는 JPA가 자동을 처리 함!연관관계 제거
private static void deleteRelation(EntityManager em) {
	Member member1 = em.find(Member.class, "member1");
	member1.setTeam(null); //연관관계 제거
}
//실행 쿼리
UPDATE MEMBER
SET
	TEAM_ID=null, ...
WHERE
	ID='member1'
연관된 엔티티 삭제
연관관계를 먼저 끊은 뒤 삭제 해야 한다. 그렇지 않으면 외래 키 제약조건으로 인해 DB 오류 발생
member1.setTeam(null) //회원1 연관관계 제거
member2.setTeam(null) //회원2 연관관계 제거
em.remove(team); //팀 제거 양방향 연관관계 매핑
@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;
	}
	//Getter, Setter ..
}
@Entity
public class Team {
	@Id
	@Column(name="TEAM_ID")
	private String id;
	private String name;
	
	@OneToMany(mappedBy = "team") //연관관계의 주인인 Member.team
	private List<Member> members = new ArrayList<Member>();
	//Getter, Setter ...
}
[테이블 연관관계]
MEMBER 와 TEAM 테이블은 N:1 관계
MEMBER              
MEMBER_ID (PK)
TEAM_ID (FK)
USERNAME
TEAM
TEAM_ID(PK)
NAME일대다 컬렉션 조회
public void biDirection() {
	
	Team team = em.find(Team.class, "team1");
	List<Member> members = team.getMembers(); //(팀->회원) //객체 그래프 탐색
	
	for(Member m : members) {
		System.out.println("m.username="+m.getUsername());
	}
}
//결과
//m.username=회원1
//m.username=회원2//5.2장에서 봤던 testSave()
public void testSave() {
	
	//팀1 저장
	Team team1 = new Team("team1", "팀1");
	em.persist(team1);
	//회원1 저장
	Member member1 = new Member("member1", "회원1");
	member1.setTeam(team1); //연관관계 설정 member1 -> team1 
	em.persist(member1);
	
	//회원2 저장
	Member member2 = new Member("member2", "회원2");
	member2.setTeam(team1); //연관관계 설정 member2 -> team1
	em.persist(member2);
}
-----------------------------------------
//SELECT * FROM MEMBER; 결과
MEMBER_ID | USERNAME | TEAM_ID
member1 | 회원1 | team1
member2 | 회원2 | team1
-----------------------------------------
team1.getMembers().add(member1) // 무시 됨 (team1은 연관관계의 주인이 아니다.)
team1.getMembers().add(member2) // 무시 됨 (team1은 연관관계의 주인이 아니다.)
-----------------------------------------
member1.setTeam(team1); //위 내용 대로 연관관계 설정 됨(Member가 연관관계의 주인)
member2.setTeam(team1); //위 내용 대로 연관관계 설정 됨(Member가 연관관계의 주인)흔한 실수
연관관계의 주인에 값을 입력하지 않고, 주인이 아닌 곳에만 값을 입력 하는 실수 (DB 에 외래 키 값이 정상적으로 저장되지 않을 시 이를 먼저 의심해보자)
연관관계 주인이 아닌 곳에만 값을 설정 시 어떻게 될까?
public void testSaveNonOwner() {
	
	//회원1 저장
	Member member1 = new Member("member1","회원1");
	em.persist(member1);
	//회원2 저장
	Member member2 = new Member("member2","회원2");
	em.persist(member2);
	
	Team team1 = new Team("team1","팀1");
	//주인이 아닌곳만 연관관계 설정
	team1.getMembers().add(member1);
	team1.getMembers().add(member2);
	em.persist(team1); 
}
//SELECT * FROM MEMBER; 결과
MEMBER_ID | USERNAME | TEAM_ID
member1 | 회원1 | null
member2 | 회원2 | null연관관계의 주인이 아닌 Team.members에만 값을 지정했기 때문에 TEAM_ID 에는 null 값이 입력됨
강조: 연관관계의 주인만이 외래 키의 값을 변경 할 수 있다.
순수한 객체까지 고려한 양방향 연관관계
객체 관점에서 양쪽방향에 모두 값을 입력 해 주는 것이 가장 안전하다!
양쪽 모두 값을 입력하지 않을 시 JPA를 사용하지 않는 순수 객체에서 심각한 문제가 발생 할 수 있음
예시
public void test순수한객체_양방향() {
	
	//팀1
	Team team1 = new Team("team1", "팀1");
	Member member1 = new Member("member1", "회원1");
	Member member2 = new Member("member2", "회원2");
	member1.setTeam(team1) //연관관계 설정  member1->team1
	member2.setTeam(team1) //연관관계 설정  member2->team1
	List<Member> members = team1.getMembers();
	System.out.println("members.size = " + members.size());
}
//결과 : members.size = 0양쪽 모두 관계를 설정한 예시
public void test순수한객체_양방향() {
	//팀1
	Team team1 = new Team("team1", "팀1");
	Member member1 = new Member("member1", "회원1");
	Member member2 = new Member("member2", "회원2");
	
	member1.setTeam(team1) //연관관계 설정  member1->team1
	team1.getMembers().add(member1) //연관관계 설정 team1->member1
	member2.setTeam(team1) //연관관계 설정  member2->team1
	team1.getMembers().add(member2) //연관관계 설정 team1->member2
	List<Member> members = team1.getMembers();
	System.out.println("members.size = " + members.size());
	
}
//결과 : member.size = 2결론 : 객체의 양방향 연관관계는 양쪽 모두 관계를 맺어주도록 하자
연관관계 편의 메소드
위에 설정한 두 관계설정 간 실수로 하나만 호출 하여 양방향이 깨질 수 있다.
따라서 양방향 관계에서 두 코드는 하나인 것처럼 사용하는 것이 안전하다.
public class Member {
	private Team team;
	public void setTeam(Team team) {
		this.team = team;
		team.getMembers().add(this);
	}
	...
}연관관계 편의 메소드 작성 시 주의사항
member1.setTeam(teamA);
member1.setTeam(teamB);
Member findMember = teamA.getMember(); //member1이 여전히 조회된다. 버그!member1.setTeam(teamA)호출 한 직후 객체 연관관계
member1 <-------> teamA
		   teamB
[삭제되지 않은 관계 1]member1.setTeam(teamB)을 호출 한 직후 객체 연관관계
member1 <---------- teamA
   ^
   |-------------->  teamB
[삭제되지 않은 관계 2]teamB 로 변경 할 때 teamA → member1의 관계를 제거하지 않았기 떄문에 연관관계 변경 시 기존 팀이 있으면 기존 팀과 회원의 연관관계를 삭제하는 코드를 추가 해야 한다!
public void setTeam(Team team) {
	
	//기존 팀과 관계를 제거
	if(this.team !=null) {
		this.team.getMembers().remove(this);
	}
	this.team = team;
	team.getMembers().add(this);
}정리하자면 객체에서 양방향 연관관계를 사용하려면 로직을 견고하게 작성 해야 한다!
참고