Reference. 자바 ORM 표준 JPA 프로그래밍
책 목차 및 이전 글
더보기
들어가기 전 JPA 특징, Q&A
1. JPA 소개
3. 영속성 관리
4. 엔티티 매핑
4.1 - 4.3 @Entity, @Table, 다양한 매핑
4.4 - 4.5 데이터베이스 스키마 자동 생성, DDL 생성 기능
5. 연관관계 매핑 기초
6. 다양한 연관관계 매핑
6.4 다대다[N:N]
- 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
- 다대다 관계 → 일대다, 다대일 관계로 풀어내는 연결 테이블을 사용
- 객체는 테이블과 다르게 객체 2개로 다대다 관계를 만드는 것이 가능 (컬렉션 <-> 컬렉션)
6.4.1 다대다: 단방향
다대다 단방향 Member
@Entity
public class Member {
@Id @Column(name = "MEMBER_ID");
private String id;
private String username;
@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT",
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<Product>();
...
}
다대다 단방향 Product
@Entity
public class Product {
@Id @Column(name = "PRODUCT_ID")
private String id;
private String name;
...
}
- 회원엔티티 - 상품 엔티티를 @ManyToMany로 매핑
- @ManyToMany와 @JoinTable을 사용해서 연결 테이블을 바로 매핑
회원과 상품을 연결하는 회원_상품(
Member_Product
) 없이 매핑 완료
@JoinTable의 속성
@JoinTable.name
: 연결 테이블을 지정, 위에 예제에선MEMBER_PRODUCT
테이블을 선택
@JoinTable.joinColumns
: 현재 방향인 회원에서 매핑할 조인 컬럼을 지정 (MEMBER_ID
)
@JoinTable.inverseJoinColumns
: 반대 방향인 상품과 매핑할 조인 컬럼을 지정 (PRODUCT_ID
)
다대다 단방향 저장
public void save() {
Product productA = new Product();
productA.setId("productA");
productA.setName("상품A");
em.persist(productA);
Member member1 = new Member();
member1.setId("member1");
member1.setUsername("회원1");
member1.getProducts().add(productA); //연관관계 설정
em.persist(member1);
}
//실행 SQL
INSERT INTO PRODUCT ...
INSERT INTO MEMBER ...
INSERT INTO MEMBER_PRODUCT ...
다대다 단방향 탐색
public void find() {
Member member = em.find(Member.class, "member1");
List<Product> products = member.getProducts(); //객체그래프 탐색
for (Product product : products){
System.out.println("product.name = " + product.getName());
}
}
//실행 SQL
SELECT * FROM MEMBER_PRODUCT MP
INNER JOIN PRODUCT P ON MP.PRODUCT_ID=P.PRODUCT_ID
WHERE MP.MEMBER_ID = ?
6.4.2 다대다:양방향
- 역방향도 @ManyToMany를 사용, 양쪽 중 원하는 곳에 mappedBy로 연관관계의 주인을 지정
- 양방향 연관관계를 만들었으므로 역방향으로 객체 그래프 탐색이 가능
//다대다 양방향 연관관계 설정
member.getProducts().add(product);
product.getMembers().add(member);
//양방향 연관관계는 편의 메소드를 추가하는 것이 편리
public void addProduct(Product product) {
...
products.add(product);
product.getMembers().add(this);
}
//역방향 탐색
public void findInverse() {
Product product = em.find(Product.class, "productA");
List<Member> members = product.getMembers();
...
}
6.4.3 다대다: 매핑의 한계와 극복, 연결 엔티티 사용
@ManyToMany
로 자동으로 연결테이블을 처리하면 편리하지만 실무에서 사용하기 어려움- 연결 테이블에 단순히 아이디만 담는 것이 아닌 주문수량, 날짜 등 추가로 컬럼을 필요로 함
- 추가 컬럼 문제를 해결하기 위해 다대다 → 일대다, 다대일 관계로 풀어야 해결 가능
Member
@Entity public class Member { @Id @Column(name = "MEMBER_ID") private String id; //역방향 @OneTomany(mappedBy = "member") private List<MemberProduct> memberProducts; ... }
Product
@Entity public class Product { @Id @Column(name = "PRODUCT_ID") private String id; ... }
MemberProduct
@Entity @IdClass (MemberProductId.class) public class MemberProduct { @Id @ManyToOne @JoinColumn(name = "MEMBER_ID") private Member member; //MemberProductID.member와 연결 @Id @ManyToOne @JoinColumn(name = "PRODUCT_ID") private Product product; //MemberProductId.product와 연결 private int orderAmount; ... }
MemberProductId
public class MemberProductId implements Serializable { private String member; //MemberProduct.member와 연결 private String product; //MemberProduct.product와 연결 //hashCode and equals Override }
복합 기본 키
- 복합 기본 키(복합키) : 기본키가 MEMBER_ID와 PRODUCT_ID같이 여러개로 이뤄진 것을 의미
- JPA에서 복합 키를 사용하면 별도 식별자 클래스 생성이 필요
- @IdClass를 사용해서 식별자 클래스를 지정
<복합 키를 위한 식별자 클래스의 특징>
- 복합 키는 별도의 식별자 클래스를 생성
- Serializable 구현 필요
- equals와 hashCode 메소드를 구현
- 기본 생성자가 필요
- 식별자 클래스는 public이어야 함
- @IdClass를 사용하는 방법 외에 @EmbeddedId를 사용하는 방법도 존재
식별 관계
부모 테이블의 기본 키를 받아 자신의 기본 키 + 외래 키로 사용하는 것을 DB 용어로 식별 관계
회원상품은 회원의 기본 키 + 상품의 기본 키 = 복합 기본 키로 사용 (동시에 외래키로 사용)
저장하는 코드
public void save() {
//회원 저장
Member member1 = new Member();
member1.setId("member1");
member1.setUsername("회원1");
em.persist(member1);
//상품 저장
Product productA = new Product();
productA.setId("productA");
productA.setName("상품1");
em.persist(productA);
//회원상품 저장
MemberProduct memberProduct = new MemberProduct();
memberProduct.setMember(member1); //주문 회원 - 연관관계 설정
memberProduct.setProduct(productA); //주문 상품 - 연관관계 설정
memberProduct.setOrderAmount(2); //주문 수량
em.persist(memberProduct);
}
조회 코드
- 복합 키는 항상 식별자 클래스를 생성하고
em.find()
에 식별자 클래스로 엔티티를 조회
- 복합 키의 사용은 복잡,
@IdClass
또는@EmbeddedId
가 필요하고 식별자 클래스도 구현 필요
public void find() {
//기본 키 값 생성
MemberProductId memberProductId = new MemberProductId();
memberProductId.setMember("member1");
memberProductId.setProduct("productA");
MemberProduct memberProduct = em.find(MemberProduct.class, memberProductId);
Member member = memberProduct.getMember();
Product product = memberProduct.getProduct();
//결과 출력 코드
...
}
6.4.4 다대다:새로운 기본키 사용
- 추천하는 기본키 생성 전략은 DB에서 자동으로 생성해주는 대리 키를 Long으로 사용하는 방법
- 이것을 사용하면 간편하고 영구히 사용가능, 비즈니스에 의존적이지 않는 장점
- 복합키(
MEMBER_ID
,PRODUCT_ID
) 대신ORDER_ID
인 대리키를 사용@Entity public class Order { @Id @GeneratedValue @Column(name = "ORDER_ID") private Long Id; @ManyToOne @JoinColumn(name = "MEMBER_ID") private Member member; @ManyToOne @JoinColumn(name = "PRODUCT_ID") private Product product; private int orderAmount; ... }
저장 코드
- 복합키를 사용했을 때와 차이는 없음
public void save() { //회원 저장 Member member1 = new Member(); member1.setId("member1"); member1.setUsername("회원1"); em.persist(member1); //상품 저장 Product productA = new Product(); productA.setId("productA"); productA.setName("상품1"); em.persist(productA); //주문 저장 Order order = new Order(); order.setMember(member1); //주문 회원 - 연관관계 설정 order.setProduct(productA); //주문 상품- 연관관계 설정 order.setOrderAmount(2); //주문 수량 em.persist(order); }
조회 코드
- 식별자 클래스 없이 고유 아이디로 조회 가능
public void find() { Long orderId = 1L; Order order = em.find(Order.class, orderId); Member member = order.getMember(); Product product = order.getProduct(); //결과 출력 코드 ... }
6.4.5 다대다 연관관계 정리
- 식별 관계 : 받아온 식별자를 기본 키 + 외래 키로 사용
- 비식별 관계 : 받아온 식별자는 외래 키로만 사용, 새로운 식별자를 추가
비식별 관계를 사용하는 것이 복합 키 없이 단순하고 편리한 ORM 매핑이 가능 이러한 이유로 추천
'개발서적 > 자바 ORM 표준 JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 7.2 @MappedSuperclass (0) | 2021.08.18 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 7.1 상속 관계 매핑 (0) | 2021.08.18 |
[자바 ORM 표준 JPA 프로그래밍] 6.3 일대일 (0) | 2021.08.18 |
[자바 ORM 표준 JPA 프로그래밍] 6.2 일대다 (0) | 2021.08.18 |
[자바 ORM 표준 JPA 프로그래밍] 6.1 다대일 (0) | 2021.08.18 |