고급 매핑
JPA는 RDB의 테이블과 매핑된 객체(Entity)를 객체답게 사용할 수 있도록 여러가지 고급 매핑 전략을 제공해줍니다.
상속관계매핑
조인테이블 전략

@Entity @Table(name = "item") @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 public class Food extends Item { private String chef; } @Entity public class Car extends Item { private long power; } @Entity public class Furniture extends Item { private long width; private long height; }
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; }
create table item (DTYPE varchar(31) not null, id bigint not null, price integer not null, stockQuantity integer not null, power bigint, chef varchar(255), height bigint, width bigint, order_item_id bigint, primary key (id))
@MappedSupserclass
@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); } }
create table orders (id varchar(255) not null, created_at TIMESTAMP, created_by varchar(255), memo clob, order_datetime TIMESTAMP, orderStatus varchar(255), member_id bigint, primary key (id))
식별자 클래스
JPA에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 한다.
public class Member { @Id private String id1; @Id private String id2; // runtime error }
@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()); }
- Serializable 인터페이스를 구현해야 한다.
- eqauls, hashCode를 구현해야 한다.
- 기본 생성자가 있어야 한다.
- 식별자 클래스는 public 이어야 한다.
@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()); }
- Serializable 인터페이스를 구현해야 한다.
- eqauls, hashCode를 구현해야 한다.
- 기본 생성자가 있어야 한다.
- 식별자 클래스는 public 이어야 한다.
- @Embeddable 애노테이션이 있어야 한다.