7. 프록시와 연관관계 관리
1. 프록시
- em.find() vs em.getReference()
- em.find(): 데이터 베이스를 통해서 실제 엔티티 객체 조회
- em.getReference(): 데이터 베이스 조회를 미루는 가짜(프록시) 객체 조회
Member reference = em.getReference(Member.class, 1l); // 실제 호출 되지는 않고 가짜 객체 생성
System.out.println("findMember = " + reference.getUsername()); // 이때 값을 DB에서 가져와야 하기 떄문에 DB 조회
System.out.println("findMember = " + reference.getId());
- 프록시 특징
- 프록시 객체는 처음 사용할 때 한번만 초기화
- 프록시 객체를 초기화할 때 프록시 객체가 실제 엔티티로 바뀌는 것이 아님, 초기화 되면 프록시 객체를 통해 실제 엔티티에 접근 가능
- 프록시 객체는 원본 엔티티를 상속 받음, 따라서 타입 체크시 주의해야함( == 비교 실패, 대신 instance of 사용)
Member m1 = em.find(Member.class, member1.getId()); // 실제 호출
Member m2 = em.getReference(Member.class, member2.getId()); // 실제 호출
System.out.println("m1 == m2" + (m1.getClass() == m2.getClass())); // false 반환
private static void logic(Member m1, Member m2) {
System.out.println("m1 == m2" + (m1 instanceof Member)); // true 반환
System.out.println("m1 == m2" + (m2 instanceof Member)); // true 반환
}
- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference를 호출해도 실제 엔티티 반환
Member m1 = em.find(Member.class, member1.getId()); // 실제 호출
System.out.println("m1.getClass() = " + m1.getClass());
Member reference = em.getReference(Member.class, member1.getId());
System.out.println("reference.getClass() = " + reference.getClass());
System.out.println("a == a:" + (m1 == reference)); // true 반환
>> 위에서 참조한 값이기 때문에 둘다 프록시객체가 아닌 엔티티 객체로 생성
- 프록시로 한번 반환을 하면 그 다음 em.find()를 해도 프록시를 반환
Member refMember = em.getReference(Member.class, member1.getId());
System.out.println("refMember.getClass() = " + refMember.getClass()); // 프록시 호출
Member findMember = em.find(Member.class, member1.getId());
System.out.println("findMember.getClass() = " + findMember.getClass()); // 프록시 반환
System.out.println("a == a:" + (refMember == findMember)); // true 반환
- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때 초기화면 문제 발생
Member refMember = em.getReference(Member.class, member1.getId());
System.out.println("refMember.getClass() = " + refMember.getClass()); // 프록시 호출
em.detach(refMember); // or em.close()
refMember.getUsername();
>> 에러 발생
- 프록시 객체 초기화
- 프록시 확인
- System.out.println("refMember" = emf.getPersistenceUnitUtil().isLoaded(refMember));
- 프록시 인스턴스의 초기화 여부 확인
- System.out.println("refMember =" + refMember.getClass())
- 프록시 클래스 확인 방법
- Hibernate.initialize(refMember);
- 강제 초기화
- JPA 표준에는 강제 초기화가 없음
- 실제 프록시의 getReference()는 많이 사용하지 않음
- System.out.println("refMember" = emf.getPersistenceUnitUtil().isLoaded(refMember));
2. 즉시 로딩과 지연 로딩
- 지연 로딩
@ManyToOne(fetch = FetchType.LAZY) //지연로딩
@JoinColumn(name = "TEAM_ID",insertable = false, updatable = false)
private Team team;
>> 프록시 타입으로 조회
- 즉시 로딩
@ManyToOne(fetch = FetchType.EAGER) // 즉시로딩
@JoinColumn(name = "TEAM_ID",insertable = false, updatable = false)
private Team team;
>> 즉시 엔티티 객체로 조회
- 가급적 지연 로딩만 사용
- 즉시 로딩을 사용하면 연동되어 있는 수십개의 테이블 여러 개를 한번에 가지가옴
- 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생
- 즉시 로딩은 JPQL에서 n+1 문제를 일으킨다.
- ManyToOne, OneToOne은 기본이 즉시 로딩으로 되어있다. ->Lazy로 설정
3. 영속성 전이:CASECADE
- 특정 엔티티를 영속 상태로 만들 떄 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때
- ex) 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장
- 영속성 전이는 연관관계를 저장할 때 자식 엔티티도 함께 저장
- 종류
- All
- PERSIST
- REMOVE
- MERGE
- REFRESH
- DETACH
- 게시판 및 첨부 파일 같은 경우 사용 가능 -> 하나의 부모가 모든 자식을 관리 할 때(소유자가 하나)
- 그러나 그 첨부 파일을 다른 게시판 즉 다른 부모가 관리하면 사용 X
4. 고아 객체
- 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
- orphanRemoval = true
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> children = new ArrayList<>();
----------------------------------------------------------
Parent parent1 = em.find(Parent.class, parent.getId());
parent1.getChildren().remove(0);
- 참조하는 곳이 하나일 때 사용해야함
- 특정 엔티티가 개인 소유할 때 사용
- 이는 부모를 제거할 때 자식이 자동적으로 삭제됨
- CASCADE의 REMOVE 처럼 사용 가능
Author And Source
이 문제에 관하여(7. 프록시와 연관관계 관리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@homelala/7.-프록시와-연관관계-관리저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)