자바 ORM 표준 JPA 프로그래밍 - 기본편 - sec05
출처 : JPA 기본편
나는 이번 강의를 통해 느꼈다. 데이터베이스 이 망할놈.
궁극적인 목표 : 객체의 참조와 테이블의 외래 키를 매핑하자!
알고가면 좋은 친구들
1. 방향 : 단방향, 양방향
2. 다중성 : 다대일, 일대다, 일대일, 다대다
3. 연관관계의 주인 : 객체 양방향 연관관계는 관리 주인이 필요
객체 지향 설계의 목표는 자율적인 객체들의 **협력 공동체**를 만드는 것
우선, 객체를 테이블에 맞추어 모델링 할 때(연관관계가 없는 객체)
이 상황을 그대로 코드로 작성을 해보면
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@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.setUsername("member1");
member.setTeamId(team.getId); //이 부분이 객체지향스럽지 않음
em.persist(member);
그리고 얘를 조회할 때는 always 귀찮은 일을 반복하게 됨 => 연관관계가 없으니깐 일일이 데베한테 물어와서 가져오고 무한 루트
Member findMember = em.find(Member.class, member.getId());
Long findTeamId = findMember.getTeamId();
Team findTeam = em.find(Team.class, findTeamId);
즉, 결론을 이야기 하자면.
객체를 테이블에 맞추어 데이터 중심으로 모델링을 하면, 협력관계를 만들 수 없음
WHY?!!!!!
1. 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾음
2. 반면, 객체는 참조를 사용해서 연관된 객체를 찾음
=> 거의 뭐 그냥 따로 놀자는 거지 뭐~
그럼 정말 객체지향스럽게 만들어 보자
이전과 달라진 점? 팀의 id값을 가져오지 않고 팀의 참조값을 가져왔음
코드로 작성해보자면 팀 id를 가져오는 부분이 굉장히 달라지지
// @Column(name = "TEAM_ID")
// private Long teamId;
@ManyToOne //멤버 입장에서는 many 그리고 team은 one
@JoinColumn(name = "TEAM_ID") //위의 관계일때 조인할 칼럼명을 적어줌
private Team team;
저장하는 부분도 달라짐!
Member member = new Member();
member.setUsername("member1");
member.setTeam(team); //바로 team을 넣어버리는 거지
//단방향 연관관계가 설정되고, 참조를 저장하는 과정
조회도 훨씬 간단해 지지!
Member findMember = em.find(Member.class, member.getId());
Team findTeam = findMember.getTeam(); //바로 id를 끄집어 내줌
위의 코드로 하면 멤버에서 팀은 가져올 수 있지만, 팀에서는 멤버를 가져올 수 없음
그래서 이걸 가능하게 만들어보자! => 양방향 매핑!
테이블의 연관관계는 외래키 하나로 양방향이 다 있는 거임!
솔직히 말해서, 테이블의 연관관계에 입장에서 방향이라는 개념이 없음
왜? 그냥 외래키 하나가 있으면 양쪽을 다 알 수 있음
이제 코드로 작성해보자!
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team") //무엇으로 매핑되어 있는지 보여줌
//team은 변수명, 즉 나의 반대편 사이드의 무엇과 걸려있는지 작성
private List<Member> members = new ArrayList<>();
//ArrayList로 초기화를 해두면 add할 때 null point가 안 뜸
객체와 테이블 간에 연관관계를 맺는 차이를 이해하자
그 차이가 무엇이죠?
즉, 객체의 양방향 관계는 사실 양방향이 아니라 서로 다른 단방향 관계 2개
=> 객체를 양방향으로 참조하려면 단방향 연관관계를 2개를 만들어야 함!
반대로, 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리!
SELECT * FROM MEMBER M JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
그래서 객체는 서로 다른 단방향 관계를 2개를 생성하는 만큼, 둘 중 하나로 외래 키를 관리해야 함!
만약에, 우리가 팀을 바꾸거나 멤버를 바꾸고자 할 때, Member에 있는 team을 바꿔야 할 지, 아니면 Team에 있는 members를 바꿔야 할 지 딜레마에 빠지게 됨
=> 그래서 우리는 주인을 정해야 함
객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 함
연관관계의 주인만이 외래 키를 관리(등록 수정)
<-> 주인이 아닌 쪽은 읽기만 가능
주인은 mappedBy 속성 사용 ❌(mappedBy 뜻 자체가 나 무언가에 의해 매핑 당했어라는 뜻이니깐 참고) <-> 주인이 아니면 mappedBy를 통해 주인을 지정
외래 키가 있는 곳을 주인을 하자!
우리가 양방향을 매핑할 때 참고한 사진을 보면, 외래키가 MEMBER 테이블에 들어가 있음 => 즉 객체를 설계할 때도 Member객체에 team을 주인으로 설정해야 함
데이터베이스에 입장으로 볼때, 외래키가 있는 곳이 무조건 '다/N', 아닌 곳은 '일'
=> N쪽이 무조건 연관관계의 주인이 되는 겨!
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member);
em.persist(member);
DB로 갔을 때의 결과는 TEAM_ID가 null값이 나옴
왜 그럴까?
team.getMembers()부분은 mappedby속성을 사용하는 즉 주인이 아닌 곳으로 읽기 전용임! 그래서 여기에 값을 넣는다고 해도 외래키가 수정이 안됨! => null값이 들어가게 됨
그럼 어떻게 작성해야할까?
연관관계의 주인에 값을 넣어줘야 한다!!!!
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member);
//연관관계의 주인에 값 설정
member.setTeam(team); //**
em.persist(member);
=> 원래 같으면 team.getMembers().add(member)
라는 코드도 적어둬야 하는데 인간은 망각의 동물이다 보니 자주 까먹을 수 있어서 차라리 setTeam에서 참조값을 넣어줄 때 한 번에 처리할 수 있도록 메서드를 추가하자
public void SetTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
근데 이 편의 메서드도 양쪽에 다 있으면 문제가 발생할 수 있으니, 한 쪽에만 남겨두자~
예를 들어, toString(), lombok, JSON 생성 라이브러리 등에서 문제가 발생할 가능성이 매우 high 함
단방향 매핑만으로도 이미 연관관계 매핑은 완료됨!
양방향 매핑은 그저 반대 방향으로 조회하는 기능이 추가된 것일 뿐이다!
즉, 단방향 매핑을 잘 해두고 양방향은 필요할 때 추가하면 된다!
=> 테이블에 영향을 주지 않음(아까도 봤듯이 테이블은 바뀌는게 없다!)
객체 입장에서는 양방향으로 해봤자 고민할 것만 많아지고 힘들어 질 뿐