자바 ORM 표준 JPA 프로그래밍 스터디 - 3주차

큰모래·2023년 5월 7일
0

6장. 다양한 연관관계 매핑

다대일

@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;
				if(!team.getMembers().contains(this)) {
						team.getMembers().add(this);
				}
		}
}

@Entity
public class Team {
		
	@Id
	@Column(name = "TEAM_ID")
	private String id;

	private String name;

	@OneToMany(mappedBy = "team")
	private List<Member> members = new ArrayList<>();
}
  • 양방향은 외래 키가 있는 쪽이 연관관계의 주인이다.
  • 양방향 연관관계는 항상 서로를 참조해야 한다.
    • 편의 메서드는 양쪽에 다 작성하면 무한루프에 빠질 수 있으므로 한 곳에만 작성한다.
    • 또한, 무한루프에 빠지지 않도록 검사하는 로직도 추가할 수 있다.

일대다

일대다 단방향

  • 팀은 여러 회원들을 참조하지만, 회원은 팀을 참조하지 않는다.
  • 외래 키는 항상 다에 있기 때문에, 반대쪽 테이블에 있는 외래 키를 관리한다.
@Entity
public class MEMBER {
		
		@Id
		@Column(name = "MEMBER_ID")
		private String id;

		private String username;

}

@Entity
public class Team {
		
	@Id
	@Column(name = "TEAM_ID")
	private String id;

	private String name;

	@OneToMany
	@JoinColumn(name = "TEAM_ID")
	private List<Member> members = new ArrayList<>();
}
  • @JoinColumn 을 명시하지 않으면, JPA는 조인 테이블을 기본 전략으로 사용해서 매핑한다.
  • 반대 테이블에 외래 키가 있기 때문에, 연관관계 처리를 위한 UPDATE 쿼리가 추가 발생한다.
Member member1 = new Member("member1");
Member member2 = new Member("member2");

Team team1 = new Team("team1");
team1.getMembers().add(member1);
team1.getMembers().add(member2);

em.persist(member1);
em.persist(member2);
em.persist(team1);

transaction.commit();

//INSERT member1 
//INSERT member2 
//INSERT team1 
//회원 테이블에 있는 외래 키 Team.id를 갱신하기 위해 팀 엔티티의 Team.members를 이용해야 한다.
//이를 통해 UPDATE 쿼리가 두 번 발생
  • 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자

일대일

주 테이블에 외래 키

  • 회원 테이블에 외래 키를 가지고 있는 상황

단방향

  • 다대일 단방향과 거의 비슷하다.
@Entity
public class Member {
		
		@Id @GeneratedValue
		@Column(name = "MEMBER_ID")
		private Long id;

		private String username;

		@OneToOne
		@JoinColumn(name = "LOCKER_ID")
		private Locker locker;
}

@Entity
public class Locker {
		
		@Id @GeneratedValue
		@Column(name = "LOCKER_ID")
		private Long id;

		private String name;
}

양방향

@Entity
public class Member {
		
		@Id @GeneratedValue
		@Column(name = "MEMBER_ID")
		private Long id;

		private String username;

		@OneToOne
		@JoinColumn(name = "LOCKER_ID")
		private Locker locker;
}

@Entity
public class Locker {
		
		@Id @GeneratedValue
		@Column(name = "LOCKER_ID")
		private Long id;

		private String name;

		@OneToOne(mappedBy = "locker")
		private Member member;
}

대상 테이블에 외래 키

  • 사물함 테이블에 외래 키를 가지고 있는 상황

단방향

  • 할 수 없다.

양방향

  • 일대일 관계에서 대상 테이블에 외래 키를 두고 싶으면 양방향으로 매핑한다.
@Entity
public class Member {
		
		@Id @GeneratedValue
		@Column(name = "MEMBER_ID")
		private Long id;

		private String username;

		@OneToOne(mappedBy = "member")
		private Locker locker;
}

@Entity
public class Locker {
		
		@Id @GeneratedValue
		@Column(name = "LOCKER_ID")
		private Long id;

		private String name;

		@OneToOne
		@JoinColumn(name = "MEMBER_ID")
		private Member member;
}

다대다

다대다 관계는 정규화된 테이블 2개로 표현할 수 없다.

중간에 연결 테이블을 추가해야 한다.

Member - Member_Product - Product

  • MemberProduct는 다대다 관계
  • Member_Product는 회원이 주문한 상품을 의미한다.

객체는 테이블과 다르게 컬렉션을 통해 객체 2개로 다대다 표현이 가능하다.

다대다 단방향

  • @JoinTable을 통해 연결 테이블과 매핑했다.
    • 이렇게 함으로써, Member_Product 엔티티를 따로 생성할 필요가 없어진다.
  • @JoinTable.name : 연결 테이블명
  • @JoinTable.joinColumns : 회원과 매핑할 조인 칼럼 정보 지정
  • @JoinTable.inverseJoinColumns : 반대 방향인 상품과 매핑할 조인 칼럼 정보 지정
  • 회원 객체를 만들어서 저장할 때 연결 테이블에도 값이 저장된다.
@Entity
public class Member {
		
		@Id @GeneratedValue
		@Column(name = "MEMBER_ID")
		private Long 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>();
}

@Entity
public class Product {
		
		@Id @GeneratedValue
		@Column(name = "PRODUCT_ID")
		private Long id;

		private String name;
}

다대다 양방향

@Entity
public class Product {
		@Id @GeneratedValue
		@Column(name = "PRODUCT_ID")
		private Long id;

		private String name;
		
		@ManyToMany(mappedBy = "products")
		private List<Member> members;
}

다대다 매핑의 한계와 극복, 연결 엔티티 사용

  • 연결 테이블에 주문 수량과 주문 날짜 칼럼이 추가됐다 가정해보자
  • 주문 엔티티나 상품 엔티티에는 해당 칼럼들을 매핑할 수 없기 때문에 더이상 @ManyToMany를 사용 X
  • 결국, 연결 테이블을 매핑하는 연결 엔티티를 따로 만들어야 한다.

Member - MemberProduct : 일대다 관계

MemberProduct - Product : 다대일 관계

@Entity
public class Member {
		
		@Id @GeneratedValue
		@Column(name = "MEMBER_ID")
		private Long id;

		private String username;

		@OneToMany(mappedBy = "member")
		private List<MemberProduct> memberProducts;
}

@Entity
public class Product {
		
		@Id @GeneratedValue
		@Column(name = "PRODUCT_ID")
		private Long id;

		private String name;
}

@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {
		
		@Id
		@ManyToOne
		@JoinColumn(name = "MEMBER_ID")
		private Member member;

		@Id
		@ManyToOne
		@JoinColumn(name = "PRODUCT_ID")
		private Product product;

		private int orderAmount;

		...
}
public class MemberProductId implements Seriallizable {
		
		private String member;
		private String product;

		@Override
		public boolean equals(Object o) {...}

		@Override
		public int hashCode() {...}

}
  • @Id@JoinColumn을 동시에 사용해서 기본 키, 외래 키를 한번에 매핑
  • 복합 기본 키 매핑
    • JPA에서 복합 키를 사용하려면 별도의 식별자 클래스를 만들어야 한다.
    • 그리고, 엔티티에 @IdClass 를 사용해서 식별자 클래스를 지정한다.
    • 식별자 클래스 특징
      • Serializeable 구현
      • equalshashCode 구현
  • 식별 관계
    • MemberProductMemberProduct로부터 기본 키를 받아서 자신의 기본 키로 사용한다.
    • 이렇게 부모 테이블의 기본 키를 받아서 자신의 기본 키 + 외래 키로 사용하는 것을 의미한다.
//조회
MemberProductId memberProductId = new MemberProductId();
memberProductid.setMember("member1");
memberProductid.setProduct("productA");

MemberProduct memberProduct = em.find(MemberProduct.class, memberProudctId);

Member member = memberProduct.getMember();
Product product = memberProduct.getProduct();
  • 복합 키는 항상 식별자 클래스를 통해 엔티티를 조회해야 한다.
  • 복합 키를 사용하는 방식은 너무 복잡하다.

다대다 새로운 기본 키 사용

ORDER_ID라는 기본 키를 새로 만들고, MEMBER_IDPRODUCT_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;

		...
}

매핑이 단순해지고 이해하기 쉬워진 것을 확인할 수 있다.

//조회
Long orderId = 1L;
Order order = em.find(Order.class, orderId);

Member member = order.getMember();
Product product = order.getProduct();

조회도 식별자 클래스를 사용하지 않아 간편해진 것을 확인할 수 있다.

정리

  • 식별관계
    • 받아온 식별자를 기본 키 + 외래 키로 사용
  • 비식별 관계
    • 받아온 식별자는 외래 키로만 사용하고 새로운 식별자를 추가한다.
    • 객체 입장에서 보면 해당 방식을 사용하는 것이 단순하고 편리하게 ORM 매핑이 가능해진다.

7장. 고급 매핑

상속 관계 매핑

관계형 데이터베이스에는 객체의 상속 개념이 없다.

대신 슈퍼타입 서브타입 관계라는 모델링 기법이 있기 때문에, 객체의 상속 구조와 이 기법을 매핑하는 것이 ORM의 상속 관계 매핑이다.

슈퍼타입 서브타입 논리 모델을 실제 물리 모델인 테이블로 구현할 때 3가지 방법을 선택할 수 있다.

  • 각각의 테이블로 변환(조인 전략)
  • 통합 테이블로 변환(단일 테이블 전략)
  • 서브타입 테이블로 변환(테이블 전략)

조인 전략

엔티티를 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략

따라서 조회할 때 조인을 자주 사용한다. 테이블은 타입의 개념이 없으므로 타입 구분 컬럼을 추가해야 한다.

  • @Inheritance(strategy = InheritanceType.JOINED)
    • 상속 매핑은 부모 클래스에 @Inheritance를 사용해야 한다.
    • 매핑 전략은 JOIN으로 지정
  • @DiscriminatorColumn(name = "DTYPE")
    • 부모 클래스에 구분 컬럼을 지정
  • @DiscriminatorValue("M")
    • 엔티티 저장 시 구분 컬럼에 입력될 값을 지정
  • @PrimaryKeyJoinColumn(name = "BOOK_ID")
    • 자식 테이블의 기본 키 컬럼명을 기본 값이 아닌 다른 값으로 사용하고 싶을 때 설정
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    private String name;
    private int price;
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {

    private String artist;
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;
    private String actor;
}

@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "BOOK_ID")
public class Book extends Item {

    private String author;
    private String isbn;
}
  • 장점
    • 테이블 정규화
    • 저장공간의 효율성 증가
  • 단점
    • 조회 쿼리가 복잡하고 성능이 저하될 수 있다.
    • 데이터를 등록할 INSERT 쿼리를 두 번 실행한다. (부모 데이터 저장, 자식 데이터 저장)

단일 테이블 전략

  • 테이블을 하나만 사용하는 전략
  • 조회 시 조인을 사용하지 않으므로 가장 빠르다.
  • 모든 컬럼에 대해 null 값을 허용해야 한다.
  • @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    • 단일 테이블 전략을 사용
  • 테이블 하나에 모든 것을 통합하므로 구분 컬럼 필수
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {

    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    private String name;
    private int price;

}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
		...
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
		...
}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {
		...
}
  • 장점
    • 조인이 필요 없으므로 조회 속도가 빠르고 쿼리가 단순하다.
  • 단점
    • 자식 엔티티에 매핑한 컬럼은 모두 null 허용해야 한다.
    • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있어 오히려 성능이 나빠질 수도 있다.

구현 클래스마다 테이블 전략

  • 자식 엔티티마다 테이블을 만드는 전략
  • InheritanceType.TABLE_PER_CLASS
    • 구현 클래스마다 테이블 전략 사용
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {

    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    private String name;
    private int price;

}

@Entity
public class Album extends Item {
		...
}

public class Movie extends Item {
		...
}

@Entity
public class Book extends Item {
		...
}
  • 장점
    • 서브 타입을 구분해서 처리할 때 효과적
    • not null 제약조건 사용 가능
  • 단점
    • 여러 자식 테이블을 함께 조회할 때 성능 느리다. (union 사용해야함)
    • 자식 테이블을 통합해서 쿼리하기 어렵다.
    • 잘 사용 안하는 전략임

@MappedSuperclass

  • 부모 클래스는 테이블과 매핑하지 않고 자식 클래스에게 매핑 정보만 제공한다. (추상 클래스 느낌)
  • 자식 엔티티들은 상속을 통해 BaseEntity의 매핑 정보를 물려받았다.
  • 여러 엔티티에서 사용하는 공통된 속성을 효율적으로 관리할 수 있다.
@MappedSuperclass
public abstract class BaseEntity {

    @Id @GeneratedValue
    private Long id;
    private String name;
}

@Entity
public class Member extends BaseEntity {

    // ID 상속
    // NAME 상속
    private String email;
		...
}

@Entity
public class Seller extends BaseEntity {

    // ID 상속
    // NAME 상속
    private String shopName;
		...
}

복합 키와 식별 관계 매핑

복합 키 : 비식별 관계 매핑

JPA는 복합 키를 지원하기 위해 @IdClass 와 @EmbeddedId 2가지 방법을 제공한다. 

  • @IdClass
    • 관계형 데이터베이스에 가까운 방법
  • @EmbeddedId
    • 객체지향에 가까운 방법이다.

@IdClass

  • 복합 키를 매핑하기 위해 별도의 식별자 클래스가 필요하다.
  • @IdClass를 통해 ParentId 클래스를 식별자 클래스로 정의
  • 식별자 클래스의 속성명과 엔티티에서 사용하는 식별자의 속성명이 같아야 한다.
@IdClass(ParentId.class)
public class Parent {

    @Id
    @Column(name = "PARENT_ID1")
    private String id1; // ParentId.id1과 연결

    @Id
    @Column(name = "PARENT_ID2")
    private String id2; // ParentId.id2와 연결

    private String name;
		...
}

public class ParentId implements Serializable {

    private String id1;
    private String id2;

		public ParentId() {}

		public ParentId(String id1, String id2){
				this.id1 = id1;
				this.id2 = id2;
		}
		
		// equals
		// hashCode
}
  • 부모 테이블의 기본 키가 복합 키이므로 자식 테이블의 외래 키도 복합 키이다.
  • @JoinColumns를 통해 여러 컬럼을 매핑한다.
  • referencedColumnNamename과 값이 같으면 생략 가능
@Entity
public class Child {

    @Id
    private String id;

    @ManyToOne
    @JoinColumns({
            @JoinColumn(name = "PARENT_ID1",
                referencedColumnName = "PARENT_ID1"),
            @JoinColumn(name = "PARENT_ID2",
                referencedColumnName = "PARENT_ID2")
    })
    private Parent parent;
}

@EmbeddedId

  • Parent 엔티티에서 식별자 클래스인 ParentId를 직접 사용하고 @EmbeddedId를 붙여주면 된다.
  • 식별자 클래스에 @Embeddable을 붙여준다.
  • 식별자 클래스에 기본 키를 직접 매핑한다.
@Entity
public class Parent {

    @EmbeddedId
    private ParentId id;
    private String name;
}

@Embeddable
public class ParentId implements Serializable {

    @Column(name = "PARENT_ID1")
    private String id1;

    @Column(name = "PARENT_ID2")
    private String id2;

		// equals
		// hashCode
}
//저장할 때 식별자 클래스를 직접 생성해서 parent에 세팅해준다.
Parent parent = new Parent();
ParentId parentId = new ParentId("id1", "id2");
parent.setId(parentId);
parent.setName("name");
em.persist(parent);

복합 키와 equals(), hashCode()

영속성 컨텍스트는 엔티티의 식별자를 키로 사용하여 엔티티를 관리한다. 이때, equals()hashCode()를 통해 비교하기 때문에, 식별자 객체의 동등성이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 찾을 수 없는 등 심각한 문제가 발생할 수 있다. 따라서 복합 키는 equals()hashCode() 를 필수 구현해야 한다.

복합 키 : 식별 관계 매핑

@IdClass와 식별 관계

  • 식별 관계는 기본 키와 외래 키를 같이 매핑해야하므로 @Id@ManyToOne 을 같이 사용했다.
@Entity
public class Parent {

    @Id
    @Column(name = "PARENT_ID")
    private String id;
    private String name;
}

@Entity
@IdClass(ChildId.class)
public class Child {

    @Id
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    public Parent parent;

    @Id
    @Column(name = "CHILD_ID")
    private String childId;

    private String name;

}

public class ChildId implements Serializable {
    
    private String parent; // Child.parent 매핑
    private String childId; // Child.childId 매핑

    // 기본 생성자
		// equals
		// hashCode
}

@Entity
@IdClass(GrandChildId.class)
public class GrandChild {

    @Id
    @ManyToOne
    @JoinColumns({
            @JoinColumn(name = "PARENT_ID")
            @JoinColumn(name = "CHILD_ID")
    })
    private Child child;

    @Id
    @Column(name = "GRANDCHILD_ID")
    private String id;

    private String name;

}

public class GrandChildId implements Serializable {

    private ChildId child; // GrandChild.child 매핑
    private String id; // GrandChild.id 매핑

}

@EmbeddedId와 식별 관계

  • 식별 관계로 사용할 연관관계의 속성에 @MapsId를 사용한다.
    • @MapsId는 외래 키와 매핑한 연관관계를 기본 키에도 매핑하겠다는 뜻이다.
@Entity
public class Parent {

    @Id
    @Column(name = "PARENT_ID")
    private String id;
    private String name;
}

@Entity
public class Child {

    @EmbeddedId
    private ChildId id;

    @MapsId("parentId") // ChildId.parentId 매핑
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    public Parent parent;

    private String name;
}

@Embeddable
public class ChildId implements Serializable {

    private String parentid; // @MapsId("parentId")로 매핑
 
    @Column(name = "CHILD_ID")
    private String id;

		// equals
		// hashCode
}

@Entity
public class GrandChild {

    @EmbeddedId
    private GrandChildId id;

    @MapsId("childId") // GrandChildId.childId 매핑
    @ManyToOne
    @JoinColumns({
            @JoinColumn(name = "PARENT_ID"),
            @JoinColumn(name = "CHILD_ID")
    })
    private Child child;

    private String name;
}

@Embeddable
public class GrandChildId implements Serializable {

    private ChildId childid; // @MapsId("childId")로 매핑

    @Column(name = "GRANDCHILD_ID")
    private String id;

		// equals
		// hashCode
}

비식별 관계로 구현

  • 매핑도 쉽고 코드도 단순해졌다.
  • 복합 키가 없으므로 복합 키 클래스를 만들지 않아도 된다.
@Entity
public class Parent {

    @Id
    @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    private String name;
}

@Entity
public class Child {

    @Id
    @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;
}

@Entity
public class GrandChild {

    @Id
    @GeneratedValue
    @Column(name = "GRANDCHILD_ID")
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "CHILD_ID")
    private Child child;
}

일대일 식별 관계

  • 자식 테이블의 기본 키 값으로 부모 테이블의 기본 키 값만을 사용하는 특징이 있다.
  • 식별자가 하나면 @MapsId를 사용할 때 속성 값은 비워두면 된다.
    • 자동으로 식별자랑 매핑된다.
@Entity
public class Board {

    @Id
    @GeneratedValue
    @Column(name = "BOARD_ID")
    private Long id;

    private String title;

    @OneToOne(mappedBy = "board")
    private BoardDetail boardDetail;
}

@Entity
public class BoardDetail {

    @Id
    private Long boardId;

    @MapsId // BoardDetail.boardId 매핑
    @OneToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;

    private String content;
}

식별, 비식별 관계의 장단점

  • 식별 관계는 부모 테이블의 기본 키를 자식 테이블로 전파하면서 자식 테이블의 기본 키 컬럼이 점점 늘어난다. 결국 조인할 때 SQL 이 복잡해지고 기본 키 인덱스가 불필요하게 커질 수 있다.
  • 식별 관계는 2개 이상의 컬럼을 합해서 복합 기본 키를 만들어야 하는 경우가 많다.
  • 식별 관계는 부모 테이블의 기본 키를 자식 테이블의 기본 키로 사용하므로 테이블의 구조가 유연하지 못하다.
  • 일대일 관계를 제외하고 식별 관계는 복합 키를 사용한다. JPA에서 복합 키는 컬럼이 하나인 기본 키를 매핑하는 것보다 많은 노력이 필요하다.
  • 비식별 관계의 기본 키는 주로 대리 키를 사용하는데 JPA는 대리 키를 생성하기 위한 편리한 방법을 제공한다.
  • 식별 관계는 상위 테이블들의 기본 키 컬럼을 자식, 손자 테이블들이 가지고 있으므로 특정 상황에 조인 없이 하위 테이블만으로 검색을 완료할 수 있다.

조인 테이블

조인 테이블 방식은 별도의 테이블을 사용해서 여기서 두 테이블의 외래 키를 가지고 연관관계를 관리한다.

일대일 조인 테이블

  • @JoinTable
    • name : 매핑할 조인 테이블 이름
    • joinColumns : 현재 엔티티를 참조하는 외래 키
    • inverseJoinColumns : 반대 방향 엔티티를 참조하는 외래 키
@Entity
public class Parent {

    @Id
    @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    private String name;

    @OneToOne
    @JoinTable(name = "PARENT_CHILD",
            joincolumns = @JoinColumn(name = "PARENT_ID"),
            inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private Child child;
}

@Entity
public class Child {

    @Id
    @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;
}

일대다 조인 테이블

@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    private String name;

    @OneToMany
    @JoinTable(name = "PARENT_CHILD",
            joincolumns = @JoinColumn(name = "PARENT_ID"),
            inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private List<Child> children = new ArrayList<>();
}

@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;
}

다대일 조인 테이블

  • optional = false : 연관된 엔티티가 무조건 존재해야 한다.
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent")
    private List<Child> children = new ArrayList<>();

}

@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;

    private String name;

    @ManyToOne(optional = false)
    @JoinTable(name = "PARENT_CHILD",
            joincolumns = @JoinColumn(name = "CHILD_ID"),
            inverseJoinColumns = @JoinColumn(name = "PARENT_ID")
    )
    private Parent parent;
}

다대다 조인 테이블

@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    private String name;

    @ManyToMany
    @JoinTable(name = "PARENT_CHILD",
            joincolumns = @JoinColumn(name = "PARENT_ID"),
            inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private List<Child> children = new ArrayList<>();
}

@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;
}

엔티티 하나에 여러 테이블 매핑

  • @Table 을 사용해서 BOARD 테이블과 매핑
  • @SecondaryTable 을 사용해서 BOARD_DETAIL 테이블과 매핑
  • pkJoinColumns : 매핑할 다른 테이블의 기본 키 컬럼 속성
  • 잘 사용하지 않는다.
  • 두 테이블을 하나의 엔티티에 매핑하는 것보다 각각 만들어서 일대일 매핑하는 것을 권장한다.
@Entity
@Table(name = "BOARD")
@SecondaryTable(name = "BOARD_DETAIL",
    pkJoinColumns = @PrimaryKeyJoinColumn(name = "BOARD_DETAIL_ID"))
public class Board {

    @Id @GeneratedValue
    @Column(name = "BOARD_ID")
    private Long id;

    private String title;

    @Column(table = "BOARD_DETAIL")
    private String content;

}
profile
큰모래

0개의 댓글