인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

도등어님의 프로필 이미지

작성한 질문수

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

JPA 프록시 객체 AccessType.FIELD에서 getId 시 초기화?

해결된 질문

작성

·

68

·

수정됨

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)

[질문 내용]
안녕하십니까 선생님! 질문 제목을 보고 얘는 왜 이 질문을 여기에 올렸을까라고 의아해하실 수도 있는데, 사실 제가 영한님 스프링 관련 강의는 다 사서 열심히 보고 있는데(정말 도움 많이 받고 있습니다. 항상 정말 잘 듣고 있습니다!!) JPA는 영한님이 쓰신 JPA 책을 사서 보고 있습니다! 그런데 프록시 객체 파트를 읽어보던 도중 이해가 되지 않는 부분이 있어 질문을 쓰고 싶은데 JPA 쪽에 질문으로 쓰려고 해도 강의를 산 사람만 작성할 수 있어 부득이하게 여기에서 질문하게 되었습니다 죄송합니다 ㅜㅜㅜ

 

다름이 아니라

 

지금 JPA의 프록시 객체 파트를 공부하고 있습니다. 그런데 294P의 8.1.2 프록시와 식별자 부분에서 엔티티를 em.getReference, 즉 프록시로 조회할 때 엔티티 접근 방식을 AccessType.PROPERTY로 설정한 경우 getId를 호출해도 프록시를 초기화하지 않는다고 하셨습니다. 그래서 직접 한번 코드를 작성해보았습니다.

 

우선 AccessType.PROPERTY 방식부터 적용해보았습니다.

 

PROPERTY와 FIELD 두 방식 모두 실험 전 create 옵션으로 Parent 객체 하나를 만들어두고 후에 none으로 바꾸어 진행했습니다.

 

Parent 객체 생성 코드

Parent parent = new Parent();
parent.setId(1L);
parent.setName("parent1");
em.persist(parent);

 

Parent.java

@Entity
@Setter
@Getter
@Access(AccessType.PROPERTY)
public class Parent {

    private Long id;
    private String name;

    @Id
    public Long getId() {
        return id;
    }
}

JpaMain.java

public static void main(String[] args) {
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();
    
    Parent foundParent = em.getReference(Parent.class, 1L);
    System.out.println(foundParent.getClass());
    System.out.println(foundParent.getId());
    System.out.println(foundParent.getName());

    tx.commit();
    em.close();
    emf.close();
}

해당 코드를 실행시켰을 때

image.png

이렇게 나온걸 보니 확실히 getId 후에 getName 조회시 프록시 객체를 초기화(select 구문 실행)을 했다는 것을 알 수 있었습니다. 그런데 AccessType.FIELD로 조회하면 getId 를 할때에도 프록시 객체를 초기화한다고 하여 아래와 같이 Parent.java의 코드를 다시 작성하여 JpaMain.java를 실행해보았습니다.

 

Parent.java

@Entity
@Setter
@Getter
@Access(AccessType.FIELD)
public class Parent {

    @Id
    private Long id;
    private String name;
}

제 예상으로는 getId 실행시 프록시 객체를 초기화 한다고 하셨으니 실제 출력에는 Parent의 getId()가 실행되기 전에 select 문이 실행되어야 할 것이라고 예상하였습니다. 하지만

image.png

위와 같이 getId()에는 프록시 객체를 초기화하지 않는 것처럼 보여지는 출력이 나왔습니다.

책에서는 AccessType.FIELD 사용시 getId()를 호출하면 JPA는 getId()가 id만 조회하는 메소드인지 다른 필드까지 활용해서 어떤 일을 하는 메소드인지 알지 못하므로 프록시 객체를 초기화한다고 나와있는데 왜 이런 결과가 나온 것일까요? 제가 잘못 이해하고 있는 것이 있을까요?

답변 2

1

도등어님의 프로필 이미지
도등어
질문자

아하.... 원인은 hibernate.jpa.compliance.proxy 설정 때문이었습니다.

 

하이버네이트는 JPA 명세와는 다르게, 식별자를 호출할 때는 엔티티를 초기화하지 않는다고 합니다. 만약 식별자를 호출할 때 엔티티를 초기화하고 싶다면 hibernate.jpa.compliance.proxy 값을 true로 설정해주어야 한다고 하네요!

해당 설정 true 로 바꾼 후 FIELD 사용시 getId() 호출전 select 구문 실행 되는것 확인했습니다!

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다.
도등어 님의 질문에 대해 도움을 드리겠습니다.

질문 주신 내용은 JPA에서 프록시 객체와 AccessType에 관한 문제로 보입니다. AccessType.PROPERTYAccessType.FIELD의 동작 차이 때문에 결과가 달라질 수 있습니다.

1. AccessType.PROPERTY:
- 이 경우, JPA는 엔티티의 접근 방식에 프로퍼티(getter 메소드)를 사용합니다. 따라서 getId() 메소드를 활용할 때 프록시 초기화를 유도하지 않게 설정됩니다.

2. AccessType.FIELD:
- 필드 직접 접근을 설정했을 때, JPA는 해당 필드를 직접 조작하기 때문에 프로퍼티 방식에서 발생하는 최적화가 적용되지 않습니다. 하지만 여기서 혼동이 생긴 이유는 JPA 라이브러리의 버전, 설정 등에 따라 프록시 초기화 여부가 다를 수 있다는 점입니다.

위의 설명에서 이해되지 않는 부분은 JPA의 특정 버전이나 설정에 따라 동작이 다르게 나타날 수 있다는 것입니다. 또한, getId() 메소드의 내부 구현이나 엔티티 매핑 방식에 따라서 초기화 동작이 차이날 수 있습니다.

관련하여 참고할 만한 기존 답변 링크를 제공드립니다:

  1. 프록시의 getter에 대해서
  2. 프록시 질문 있습니다

이 링크들을 통해 JPA 프록시 처리 방식의 다른 사례와 설명을 확인하시기 바랍니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.