상속관계매핑
조인테이블 전략

@Entity @Table(name = "item") @Getter @Setter @Inheritance(strategy = InheritanceType.JOINED) public abstract class Item { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private int price; private int stockQuantity; @ManyToOne @JoinColumn(name = "order_item_id", referencedColumnName = "id") private OrderItem orderItem; public void setOrderItem(OrderItem orderItem) { if (Objects.nonNull(this.orderItem)) { this.orderItem.getItems().remove(this); } this.orderItem = orderItem; orderItem.getItems().add(this); } } @Entity @Getter @Setter public class Food extends Item { private String chef; } @Entity @Getter @Setter public class Car extends Item { private long power; } @Entity @Getter @Setter public class Furniture extends Item { private long width; private long height; }
@Inheritance
를 사용하기 위해서는 부모 클래스가 추상 클래스여야 함
- Join 방식은 item entity의 id를 fk로 가지는 여러 entity가 만들어지는 방법임
- 테이블이 각각 만들어짐
create table Car (power bigint not null, id bigint not null, primary key (id)) create table Food (chef varchar(255), id bigint not null, primary key (id)) create table Furniture (height bigint not null, width bigint not null, id bigint not null, primary key (id)) create table item (DTYPE varchar(31) not null, id bigint not null, price integer not null, stockQuantity integer not null, order_item_id bigint, primary key (id)) alter table Car add constraint FKge7by0el7y9941l8ssptf0hu9 foreign key (id) references item alter table Food add constraint FK9y4qp56rc3069yucfklyi6p64 foreign key (id) references item alter table Furniture add constraint FKfva03eyd5mv4v7uyj6ekjda81 foreign key (id) references item
싱글 테이블전략

@Entity @Table(name = "item") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "DTYPE") public abstract class Item { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private int price; private int stockQuantity; @ManyToOne @JoinColumn(name = "order_item_id", referencedColumnName = "id") private OrderItem orderItem; public void setOrderItem(OrderItem orderItem) { if (Objects.nonNull(this.orderItem)) { this.orderItem.getItems().remove(this); } this.orderItem = orderItem; orderItem.getItems().add(this); } } @Entity @DiscriminatorValue("FOOD") public class Food extends Item{ private String chef; } @Entity @DiscriminatorValue("CAR") public class Car extends Item{ private long power; } @Entity @DiscriminatorValue("FURNITURE") public class Furniture extends Item{ private long width; private long height; }
- @DiscriminatorColumn(name=”DTPYE”) 이런 식으로 부모 클래스에 정의해주고 자식 클래스에는 @DiscriminatorValue로 어노테이션을 붙여주어야 함
- 테이블이 여러개 생기는게 아니라, Item이라는 테이블만 생기고 그 테이블의 특정 컬럼(DTYPE) 에 Food Car, Furniture의 값이 들어가게됨
- 현업에서는 테이블 갯수 많아지면 관리 힘들어서 싱글테이블 전략을 더 많이 사용함
@MappedSuperclass
- 실제 entity가 되는 클래스는 아니지만 상속을 받게되면 자식 테이블에서 추가로 상위 클래스의 필드 값을 추가로 갖게 됨
- 이 클래스를 직접 생성해서 사용할 일은 거의 없으므로 추상 클래스로 만드는 것을 권장
@MappedSuperclass public class BaseEntity { @Column(name = "created_by") private String createdBy; @Column(name = "created_at", columnDefinition = "TIMESTAMP") private LocalDateTime cratedAt; } @Entity @Table(name = "orders") public class Order extends BaseEntity { @Id @Column(name = "id") private String uuid; @Column(name = "order_datetime", columnDefinition = "TIMESTAMP") private LocalDateTime orderDatetime; @Enumerated(EnumType.STRING) private OrderStatus orderStatus; @Lob private String memo; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", referencedColumnName = "id") private Member member; @OneToMany(mappedBy = "order") private List<OrderItem> orderItems; public void setMember(Member member) { if(Objects.nonNull(this.member)) { this.member.getOrders().remove(this); } this.member = member; member.getOrders().add(this); } public void addOrderItem(OrderItem orderItem) { orderItem.setOrder(this); } }
식별자 클래스
- JPA에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 함
public class Member { @Id private String id1; @Id private String id2; // runtime error }
- 두 개의 @Id 중 어떤것을 이용해서 동등성 비교를 해야 할지 몰라 RuntimeError 발생
@IdClass (비추) ⇒ 객체지향스럽지 않음
@Getter @Setter @Entity @IdClass(ParentId.class) public class Parent { @Id private String id1; @Id private String id2; } @EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor public class ParentId implements Serializable { private String id1; private String id2; }
@Test void multi_key_test() { EntityManager entityManager = emf.createEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); Parent parent = new Parent(); parent.setId1("id1"); parent.setId2("id2"); entityManager.persist(parent); transaction.commit(); Parent entity = entityManager.find(Parent.class, new ParentId("id1", "id2")); log.info("{}, {}", entity.getId1(), entity.getId2()); }
@IdClass 에서 지켜야할 규칙
- Serializable 인터페이스를 구현해야 한다.
- eqauls, hashCode를 구현해야 한다.
- 기본 생성자가 있어야 한다.
- 식별자 클래스는 public 이어야 한다.
- 사용할 때 @IdClass에서의 필드이름과 필드이름을 똑같이 해야 함
@EmbeddedId → 강추! 객체지향스러움
- 이걸 실무에서 조금 더 많이 쓴다고 함
@Getter @Setter @Entity public class Parent2 { @EmbeddedId private ParentId2 id; } @Getter @Setter @EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor @Embeddable public class ParentId2 implements Serializable { private String id1; private String id2; }
@Test void multi_key_test_embedded() { EntityManager entityManager = emf.createEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); Parent2 parent = new Parent2(); parent.setId(new ParentId2("id1", "id2")); entityManager.persist(parent); transaction.commit(); Parent2 entity = entityManager.find(Parent2.class, new ParentId2("id1", "id2")); log.info("{}, {}", entity.getId().getId1(), entity.getId().getId2()); }
@Embeddable
- Serializable 인터페이스를 구현해야 한다.
- eqauls, hashCode를 구현해야 한다.
- 기본 생성자가 있어야 한다.
- 식별자 클래스는 public 이어야 한다.
- @Embeddable 어노테이션 추가