나다.
이 글이 뭐하는 글이냐면
JPA는 객체와 데이터베이스의 싱크를 맞춰 객체로 데이터베이스를 쪼물딱거릴 수 있게
하는 기능을 가지고 있다.
JPA가 그 둘을 이어주는 교량의 역할을 하지만 결정적으로
"어떤 요소를 어떤 요소에 이어줄지" 명시하는 것은 필수적이다.
삼라만상 모든 일들이 같다.
이번 글에서는 그 방법에 대해 예시와 함께 알아보자.
객체를 테이블에 맞추면?
먼저, 객체를 테이블에 맞추어 모델링 한 경우에 대한 예를 보자.
우리가(You) 배가 고파 전화를 통해
동네에 하나뿐인 중국집 청룡각(BlueDragonR, 간짜장/찹쌀탕수육 밖에 할 줄 모름)에 주문을 한다고 치자.
@Entity
@Getter
@Setter
public class You {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id; // 님들 주민등록번호 정도로 생각하셈.
private String name; // 님들이 이름.
private Long 청룡각번호;
}
@Entity
@Getter
@Setter
public class BlueDragonR {
@Id
@GeneratedValue
@Column(name = "BDR_ID")
private Long id; // 청룡각 전화번호 라고 생각하셈.
private String storeName; // 청룡각 이름임.
private String 짜장면;
private String 탕수육;
}
위의 경우 어떤 방식으로 청룡각에 주문하게 될까?
BlueDragonR 청룡각 = new BlueDragonR();
청룡각.setStoreName("청룡각");
청룡각.set짜장면("간짜장");
청룡각.set탕수육("찹쌀탕수육");
em.persist(청룡각);
// 청룡각이 배달책자에 존재함.
You 님 = new You();
님.setName("님 이름");
님.set청룡각번호(청룡각.getId); //청룡각 전화번호 찾았음.
em.persist(님); // 님은 이제 청룡각 전화번호 아는 상태임.
청룡각의 전화번호를 찾아 기억하고 있는 당신은 이제 청룡각에 전화를 걸고 주문해야한다.
근데 X발 전화기가 없네?
You 님 = em.find(You.class, 님.getId());
님.get청룡각번호(); // 청룡각 번호밖에 없음. 전화기(연관관계)가 없어서 배달 못시킴.
님.get청룡각번호().get짜장면(); // 이게 안됨.
청룡각의 번호를 알지만 전화기(연관관계)가 없어 짜장면을 시킬 수 없는 개병X같은 불상사가 생겨버렸다.
물론 청룡각으로 직접 찾아가서 포장해오면 된다.
영속성 컨텍스트 안에 존재하는 해당 엔티티를 직접 찾아서 당겨오면 된다는 뜻이다.
BlueDragonR 청룡각찾아감 = em.find(BlueDragonR.class, 청룡각.getId());
청룡각찾아감.get짜장면(); // 비 맞고 걸어가서 직접 포장함.
하지만 이런 것은 배민의 시대에 살고있는 우리에게는 맞지않는 행동이다.
테이블은 외래키(FK)로 조인(Join)을 사용해 연관 테이블을 찾아내고
객체는 참조(프로퍼티 접근법)를 통해 연관된 객체를 찾아낸다.
이 테이블과 객체의 간격을 좁히기 위해 우리는 연관관계 맵핑이라는 것을 사용한다.
이제는 우리가 전화기(연관관계)를 가지고 있다는 가정과 함께
배민을 이용해 다시 청룡각에 짜장면을 배달시켜 보도록하자.
다대일(N:1)의 관계
청룡각은 동네에 하나 뿐인 중국집이다.
그러나 그것이 동네주민 한 명만 청룡각에 주문을 할 수 있다는 것을 의미하지는 않는다.
동네의 주민 여러 명이 청룡각에 주문을 할 수 있고,
청룡각은 여러 명의 주문을 수용할 수 있다.
이것이 다대일의 관계(N:1의 관계) 다.
객체 간의 연관관계를 사용하게 되면 각 객체간의 참조가 자유로워진다는 뜻이다.
배민 열면 중국집이 통째로 님 휴대폰에 들어있지않음?
그거랑 비슷함. 메뉴 자유롭게 볼 수 있음.
@Entity
@Getter
@Setter
public class You {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id; // 대충 배민 가입할때 발급된 회원번호 정도로 생각하셈.
private String name; // 님들이 배민 가입할때 입력한 닉네임
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "BDR_ID")
private BlueDragonR 청룡각; //배민에 청룡각 등록되있음. 개꿀 ㅋㅋ
}
@Entity
@Getter
@Setter
public class BlueDragonR {
@Id
@GeneratedValue
@Column(name = "BDR_ID")
private Long id; // 배민에 있는 청룡각 등록번호라 생각하셈.
private String storeName; // 청룡각 이름임.
private String 짜장면;
private String 탕수육;
}
연관관계는 각 연관관계의 어노테이션으로 부여된다.
이번에 알아 볼 다대일(N:1) 관계의 경우 @ManyToOne을 사용한다.
동네주민(You)는 Many(여러 명)이고 청룡각은 One(한 개)이다.
그래서 ManyToOne임.
그리고 JoinColumn을 통해 해당 관계를 위해 Join해야 할 컬럼을 지정한다.
대충 와꾸만 봐도 견적이 나오지 않는가?
일대일(1:1) 관계는 @OneToOne
일대다(1:N) 관계는 @OneToMany
다대다(N:N) 관계는 @ManyToMany
를 사용한다.
그럼 이제 청룡각에 배민을 통해 배달을 시켜보자.
BlueDragonR 청룡각 = new BlueDragonR();
청룡각.setStoreName("청룡각");
청룡각.set짜장면("간짜장");
청룡각.set탕수육("찹쌀탕수육");
em.persist(청룡각);
// 청룡각이 배민에 등록됨.
You 님 = new You();
님.setName("님 닉네임");
님.set청룡각(청룡각);
em.persist(님); // 배민에서 청룡각 찾음.
전과 다르게 우리는 지금 전화기("연관관계"/배민)을 가지고 있다.
그렇기 때문에
You 휴대폰가진님 = em.find(You.class, 님.getId()); // 청룡각 메인페이지 들어옴.
BlueDragonR 배민청룡각 = 휴대폰가진님.get청룡각().get짜장면(); // 고마 짜장면 한 그릇 줘보소.
의 구조가 10가능으로 변하는 것이다.
이제 님에서 청룡각에 짜장면까지 다 땡겨올 수 있음. 개이득 ㅋㅋ
이것을 우리는 객체그래프 탐색이 자유롭다고 표현한다.
"근데 님은 청룡각에 있는거 다 땡겨올 수 있는데 청룡각은 님꺼 못땡겨오면 이게 무슨 관계임?"
맞다. 그래서 다음 글에서는 양방향 맵핑에 대해 다룰 예정이다.
대충 반대편(청룡각)에 You(동네주민) List 만들고 @OneToMany 갖다 박으면 되는데
다음 글에서 자세히 설명해드림.
그냥 주문한 사람 리스트에 저장해서 청룡각도 땡겨볼 수 있게 만드는거라 보시면 됨 ㅎㅎ.
뭔 개소리인지 모르겠어요.
이해하기 어려운 것이 당연하다.
원래 연관관계 자체가 어려운 종목일 뿐더러
내가 설명을 개X같이 했기때문에 더 어려울 수 있다.
사실 나도 초보임. ㅈㅅ! ㅋㅋ!
그렇기때문에 내 글에서 얼추 이해하고 조금 더 정교하게 작성된 다른 이들의 문서
또는 인강을 통해서 더욱 정석적인 지식을 습득하시길 권장드린다.
아마 이 글로 대충이라도 이해하면 더 정교한 설명을 들을 때 이해하기 더 편하지 않을까싶다.
혹여나 글에 틀린 점이 있다면 그건 내가 허접이라 그런 것이니 이해를 부탁드린다.
그럼 컴파일 오류 없는 개발하시길 :)
'JPA' 카테고리의 다른 글
[JPA] no entitymanager with actual transaction available for current thread (5) | 2023.01.04 |
---|---|
[JPA] JPA와 PK 그리고 Id (1) | 2022.12.28 |
[JPA] 영속성 컨텍스트와 Flush (4) | 2022.12.22 |
[JPA] 영속성 컨텍스트란 무엇인가. (0) | 2022.12.22 |
[JPA] JPA란 무엇인가. (1) | 2022.12.20 |