JPA
• 영속성 컨텍스트 : 엔티티 영구 저장하는 환경
[엔티티의 생명주기]
• 비영속 (new/transient)
영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
• 영속 (managed)
영속성 컨텍스트에 관리되는 상태
• 준영속 (detached)
영속성 컨텍스트에 저장되었다가 분리된 상태
• 삭제 (removed)
삭제된 상태
[영속성 컨텍스트의 특징(이점)]
• 1차 캐시 (조회 시 1차 캐시에 데이터가 이미 있는지 확인하고, 데이터가 있으면 가져온다.)
• 동일성(identity) 보장
• 쓰기 지연(트랜잭션을 지원)
- 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
• 변경 감지(Dirty Checking)
- 엔티티 수정 변경 감지
• 지연 로딩(Lazy Loading)
- 즉시 로딩(Eager) : 관련된 엔티티를 즉시 로드하는 방식
- 부모 엔티티와 자식 엔티티 간의 일대다 관계가 있을 때, 부모 엔티티를 로드할 때 관련된 모든 자식 엔티티도 함께 로드
- @ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 설정
- N+1 문제란?
- 연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 되는 문제.
- 지연 로딩(Lazy) : 관련된 엔티티를 실제로 필요할 때 로드하는 방식
- 부모 엔티티를 먼저 로드하고, 자식 엔티티가 필요한 경우에만 로드
[플러시] : 영속성 컨텍스트의 변경내용을 데이터베이스에 반영
영속성 컨텍스트를 플러시하는 방법
• em.flush() - 직접 호출
• 트랜잭션 커밋 - 플러시 자동 호출
• JPQL 쿼리 실행 - 플러시 자동 호출
[준영속 상태로 만드는 방법]
준영속 : 영속 상태의 엔티티가 영속성 컨테스트에서 분리(detached)
영속성 컨텍스트가 제공하는 기능을 사용 못함
• em.detach(entity)
특정 엔티티만 준영속 상태로 전환
• em.clear()
영속성 컨텍스트를 완전히 초기화
• em.close()
영속성 컨텍스트를 종료
[엔티티 매핑]
@Entity
- @Entity가 붙은 클래스는 JPA가 관리, 엔티티라 한다.
- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
[주의]
- 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)
- final 클래스, enum, interface, inner 클래스 사용X
- 저장할 필드에 final 사용 X
@Table
예시 -
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Setter
@SuperBuilder
@Entity
@Table(name = "BS_T_USER_Marketing_Payment")
@IdClass(MarketingPaymentId.class)
public class UserMarketingPayment {
@Id
@Column(name = "UserIdx")
private Integer userIdx;
@Id
@Column(name = "EventIdx")
private Integer eventIdx;
@Column(name = "buyCount", nullable = false)
@Builder.Default
private Integer buyCount = 0;
}
Customer 엔티티 클래스:
@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private List<Order> orders;
}
Order 엔티티 클래스:
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNumber;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer; => Customer의 내용을 참고하고, 외래키의 컬럼 이름은 customer_id이다(orders 테이블에서).
}
@MappedSuperclass
• 테이블과 관계 없고, 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할
• 상속관계 매핑X
• 엔티티X, 테이블과 매핑X
• 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공
• 조회, 검색 불가(em.find(BaseEntity) 불가)
• 직접 생성해서 사용할 일이 없으므로 추상 클래스 권장
• 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용
• 참고: @Entity 클래스는 엔티티나 @MappedSuperclass로 지정한 클래스만 상속 가능
[프록시와 연관관계 관리]
영속성 전이: CasCade
특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들도 싶을 때
CasCade.ALL : CasCade.Persist(영속화) : 부모와 자식엔티티를 한 번에 영속화 , CasCade.Remove(제거) : 부모 엔티티가 삭제되면 자식 엔티티도 삭제
- 연관된 엔티티도 함꼐 영속화하는 편리함을 제공할뿐이다.
고아 객체
- 고아 객체 제거 : 부모 엔티티와 연관관계 끊겨진 자식 엔티티를 자동으로 삭제(orphanRemoval = true)
- 참조하는 곳이 하나일 때 사용해야함!
- 특정 엔티티가 개인 소유할 때 사용
- @OneToOne, @OneToMany만 가능
- CascadeType.REMOVE처럼 동작
orphanRemoval = true 과 CasCade.Reomove 차이 :
공통점 : 부모 엔티티를 삭제했을 경우는 CascadeType.REMOVE와 orphanRemoval=true 옵션이 동일하게 작동
차이점 : CascadeType.REMOVE만 사용했을 경우에는 부모와 자식의 관계가 끊어졌다 해서 자식을 삭제하지 않는다.
orphanRemoval=true 옵션은 부모 엔티티와 자식 엔티티의 연관관계가 끊어졌을 경우, 자식 엔티티를 모두 제거를 해줍니다.
[JPA 데이터 타입 분류]
엔티티 타입
- @Entity로 정의하는 객체
- 데이터가 변해도 식별자로 지속해서 추적 가능
값 타입(기본값, 임베디드, 컬렉션) : 동등성(equals) 비교한다.
- int, Integer, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
- 기본값 타입
- 자바 기본 타입(int, double)
- 래퍼 클래스(Integer, Long)
- String
- 임베디드 타입(새로운 값 타입 정의, 복합 값 타입)
- @Embeddable: 값 타입을 정의하는 곳에 표시
- @Embedded: 값 타입을 사용하는 곳에 표시
- 기본 생성자 필수
- 값 타입은 불변 객체(immutable object)로 설계해야함
- 불변 객체: 생성 시점 이후 절대 값을 변경할 수 없는 객체
- 컬렉션 값 타입(collection value type)
: 컬렉션을 저장하기 위한 별도의 테이블이 필요함. 실무에서는 일대다 관계를 고려함.
불변 객체 : 생성 시점 이후 절대 값을 변경할 수 없는 객체와
@MappedSuperclass vs @Embeddable
- 상속과 조합의 차이
@Transactional(readOnly = true) 이점