[스프링 JPA] WEEK 3

enxnong·2023년 9월 23일
0

김영환님의 강의 자바 ORM 표준 JPA 프로그래밍 - 기본편 보면서 공부한 내용입니다.

🏊‍♀️ 섹션 6

일대다 [1:N]

📝 일대다 단방향 매핑

  • 1이 연관관계의 주인
  • 테이블에서 항상 다수(N)쪽에 외래키가 있음
  • 객체와 테이블의 차이 때문에 반대편 테이블의 외래키를 관리하는 특이한 구조
  • @JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블을 사용함.(중간에 조인된 테이블이 하나 추가됨)

💡 정리

  • 엔티티가 관리하는 외래키가 다른 테이블에 있음
  • 연관관계 관리를 위해 추가로 UPDATE SQL을 실행함
    ✅ 결론 : 다대일 양방향 매핑을 사용하자!

일대일 [1:1]

📝 일대일 관계

  • 주 테이블이나 대상 테이블 중에 외래키 선택 가능
    • 주 테이블에 외래키
    • 대상 테이블에 외래기
  • 외래키에 데이터베이스 유니크(UNI) 제약조건 추가

다대다 [N:N]

📝 다대다 관계

  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
  • 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야함
  • 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계 가능
  • @ManyToMany 사용
  • @JoinTable로 연결 테이블 지정
  • 양방향, 단방향 매핑 가능

💡 한계

  • 실무에서 사용X
  • 연결 테이블이 연결만 하고 끝나지 않음 (추가 데이터가 들어갈 수 있는데 중간 테이블에 추가 정보를 넣을 수 없음)

🏊‍♀️ 섹션 7

상속관계 매핑

📝 상속관계 매핑

  • 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑
  • 관계형 데이터베이스는 상속관계X
  • 슈퍼-서브타입이라는 관계형 매핑이 객체상속과 유사

📝 구현 방법
1. JOINED (각각 테이블로 변환)
2. SINGLE_TABLE (통합 테이블로 변환)
3. TABLE_PER_CLASS (서브타임 테이블로 변환)

💡 어노테이션

  • @Inheritance(strategy=InheritanceType.XXX)
  • @DiscriminatorColumn(name="DTYPE")
    ✅ 테이블에 DTYPE 컬럼이 생기면서 Entity명이 들어감
  • @DiscriminatorValue("XXX")
    ✅ DTYPE에 들어가는 Entity명을 변경함

Mapped Superclass - 매핑 정보 상속

📝 Mapped Superclass

  • 공통 매핑 정보가 필요할 때 사용
    ✅ ex : 등록일, 수정일, 등록자, 수정자
  • 상속관계, 엔티티 및 테이블과 매핑X
  • 부모 클래스를 상속받는 자식 클래스에 매핑 정보만 제공
  • 조회, 검색 불가(em.find("xxxx") 불가)
  • 추상 클래스 권장

🏊‍♀️ 섹션 8

프록시

📝 프록시

  • 실제 클래스를 상속 받아서 만들어짐
  • 실제 클래스와 겉 모양은 같지만 속은 텅 비어있음
  • 사용하는 입장에서는 진짜인지 프록시인지 구분하지 않고 사용(이론상)
  • 프록시 객체는 실제 객체의 참조(target)를 보관
  • 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출
  • em.find() : 데이터베이스를 통해서 실제 엔티티 객체 조회
  • em.getReference() : 데이터베이스 조회를 미루는 가짜(프록시)엔티티 객체 조회
    ✅ 즉, 디비에 쿼리가 안날라오는데 객체가 조회되는 것
// 프록시
Member member = new Member();
member.setUsername("hello");

em.persist(member);

em.flush();
em.clear();

Member findMember = em.getReference(Member.class, member.getId());
System.out.println("findMember.getClass() = " + findMember.getClass()); // 가짜 클래스
System.out.println("findMember.getId() = " + findMember.getId());
System.out.println("findMember.getUsername() = " + findMember.getUsername());
// 실제 가져다쓰는 시점에 내부적으로 영속성 컨텍스트에 요청해서 실제 레퍼런스를 가지게됨
// => 처음에 실제 가져올 때는 db에 접근하여 레퍼런스 생성함

📝 프록시 특징

  • 프록시 객체는 처음 사용할 때 한 번만 초기화
  • 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능
  • 프록시 객체는 원본 엔티티를 상속받음. 따라서 타입 체크시 주의해야함 (== 비교 실패, 대신 instance of 사용)
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환
// 1번
Member m1 = em.find(Member.class, member1.getId());
System.out.println("member = " + m1.getClass());

Member reference = em.getReference(Member.class, member1.getId());
System.out.println("reference = " + reference.getClass());

System.out.println("a == a : " + (m1 == reference)); // 항상 true가 되어야함

// 2번
Member reference = em.getReference(Member.class, member1.getId()); 
// 프록시가 한 번 실행되면 프록시로 계속 반환됨
System.out.println("member = " + m1.getClass());

Member m1 = em.find(Member.class, member1.getId()); // proxy로 반환됨
System.out.println("reference = " + reference.getClass());

=System.out.println("a == a : " + (m1 == reference)); // 항상 true가 되어야함
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생
    (하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)
 Member m1 = em.getReference(Member.class, member1.getId());
System.out.println("member = " + m1.getClass());

em.detach(m1); // 영속성 컨텍스트에서 관리안한다고 표시
//em.close(); // 영속성 컨텍스트 종료

System.out.println("getUsername =" + m1.getUsername());

💡 프록시 확인

  • 프록시 인스턴스의 초기화 여부 확인
    ✅ PersistenceUnitUtil.isLoaded(Object entity)
  • 프록시 클래스 확인 방법
    ✅ entity.getClass().getName() 등
  • 프록시 강제 초기화
    ✅ org.hibernate.Hibernate.initialize(entity)
profile
높은 곳을 향해서

0개의 댓글