데이터 중심 설계에는 객체 연관관계가 반영되어 있지 않다(참조를 사용하고 있지 않다). 따라서 객체 그래프 탐색을 할 수 없다.
단방향 참조
class Member {
private long id;
private List<Order> orders;
}
class Order {
private String id;
}
한쪽 객체에서만 참조
양방향 참조
class Member {
private long id;
private List<Order> orders;
}
class Order {
private String id;
private Member member;
}
양쪽 객체에서 서로 참조
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
...
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
public class Order {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", referencedColumnName = "id")
private Member member;
}
객체 참조를 통해 서로를 가져오는 것을 객체 그래프 탐색이라고 한다.
@JoinColumn(name = "member_id", referencedColumnName = "id")
@JoinColumn
외래 키를 매핑 할 때 사용하는 어노테이션으로서 만약 @JoinColumn
을 명시하지 않는다면 필드명 + “_” + 참조하는 테이블의 기본 키(@Id) 컬럼명
전략에 따라 외래 키를 매핑합니다.
연관관계 편의 메서드
양방향 연관관계를 형성할 때 객체 상태를 고려하여 양쪽에 모두 값을 가지도록 설정한다. 그리고 이를 위해 사용하는 것을 연관관계 편의 메서드라고 한다.
@Entity
@Table(name = "member")
public class Member {
...
@OneToMany(mappedBy = "member")
private List<Order> orders;
public void addOrder(Order order) {
order.setMember(this);
}
}
@Entity
@Table(name = "orders")
public class Order {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", referencedColumnName = "id")
private Member member;
// 연관관계 편의 메소드
public void setMember(Member member) {
if(Objects.nonNull(this.member)) {
this.member.getOrders().remove(this);
}
this.member = member;
member.getOrders().add(this);
}
}
@Entity
@Table(name = "item")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item {
....
}
@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;
}
@Inheritance(strategy = InheritanceType.JOINED)
@Entity
@Table(name = "item")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
...
}
@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;
}
DTYPE
을 이용하여 엔티티가 어떤 테이블에 매핑이 되는지 찾는다.@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 {
...
}
복합 Primary Key가 있는 경우 사용한다.
Serializable 인터페이스를 구현해야 한다.
eqauls, hashCode를 구현해야 한다.
기본 생성자가 있어야 한다.
식별자 클래스는 public 이어야 한다.
필드 이름이 같아야 한다.
비추, 객체지향스럽지 않다.
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class ParentId implements Serializable {
private String id1;
private String id2;
}
@Getter
@Setter
@Entity
@IdClass(ParentId.class)
public class Parent {
@Id
private String id1;
@Id
private String id2;
}
Serializable 인터페이스를 구현해야 한다.
eqauls, hashCode를 구현해야 한다.
기본 생성자가 있어야 한다.
식별자 클래스는 public 이어야 한다.
@Embeddable 애노테이션이 있어야 한다.
강추, 객체지향스럽다.
@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class ParentId2 implements Serializable {
private String id1;
private String id2;
}
@Getter
@Setter
@Entity
public class Parent2 {
@EmbeddedId
private ParentId2 id;
}
fetch = FetchType.LAZY
를 이용해 가져오는 것은 다 프록시 객체이다.fetch = FetchType.LAZY
를 설정 해주어야 한다.SELECT * FROM order;
emf.getPersistenceUnitUtil().isLoaded(member)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems;
...
Order order = em.find(Order.class, uuid); // 영속 상태
OrderItem item = new OrderItem(); // 비영속상태
item.setQuantity(10);
item.setPrice(1000);
order.addOrderItem(item); // item이 영속성 전이를 통해 영속 상태로 바뀌었다
transaction.commit(); // flush()
@Entity
@Table(name = "member")
public class Member extends BaseEntity {
...
@OneToMany(mappedBy = "member", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
...
}
// 회원 조회 -> 회원의 주문 까지 조회
Member findMember = entityManager.find(Member.class, 1L);
findMember.getOrders().remove(0); // order를 제거한다. (고아객체)