inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

Hibernate Proxy객체가 실제객체로 변환이 안돼요

36

papakang22

작성한 질문수 2

0

먼저 Entity Code부터 보여드리겠습니다.

 

FieldGroupMapping(부모)


@Entity
@Table(name = "FIELD_GROUP_MAPPING")
@Getter
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FieldGroupMapping extends CreationAuditingEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "FIELD_GRP_MPPG_ID", nullable = false)
  private Long id;

  @Column(name = "FIELD_CD", length = 50, nullable = false)
  private String fieldCd;

  @ManyToOne(fetch = FetchType.EAGER, optional = false)
  @JoinColumn(name = "FIELD_CD",
      referencedColumnName = "FIELD_CD",
      insertable = false,
      updatable = false,
      foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
  private FieldMetadata field;
}

 

FieldMetadata (자식)


@Entity
@Table(name = "FIELD_METADATA")
@Getter
@SuperBuilder
@AllArgsConstructor
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "FIELD_TYPE")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FieldMetadata extends CreationAuditingEntity {

  @Id
  @Column(name = "FIELD_CD", length = 50, nullable = false)
  private String fieldCd;

  @Column(name = "LABEL", length = 50, nullable = false)
  private String label;

  @Enumerated(EnumType.STRING)
  @Column(name = "DATA_TYPE", columnDefinition = "varchar(20)", nullable = false)
  private FieldDataType dataType;

}

 

RealFieldMetadata (상속된 자식)


@Entity
@Table(name = "REAL_FIELD_METADATA")
@Getter
@SuperBuilder
@AllArgsConstructor
@DiscriminatorValue(FieldDiscriminatorValues.D_REAL)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@PrimaryKeyJoinColumn(name = "FIELD_CD")
public class RealFieldMetadata extends FieldMetadata {

  @Enumerated(EnumType.STRING)
  @Column(name = "TABLE_ALIAS", columnDefinition = "varchar(50)", nullable = false)
  private TableAlias tableAlias;

  @Column(name = "COLUMN_NAME", nullable = false, length = 50)
  private String columnName;

  @Builder.Default
  @Convert(converter = BooleanYNConverter.class)
  @Column(name = "REQUIRES_JOIN", nullable = false, columnDefinition = "char(1) default 'N'")
  private boolean requiresJoin = false;

  @Enumerated(EnumType.STRING)
  @Column(name = "JOIN_TABLE", columnDefinition = "varchar(50)")
  private TableAlias joinTable;


}


FieldGroupMapping 과 FieldMetadata는 1:N 관계입니다.

List<String> idList = List.of("GD01", "GD02", "GD03");
// fetch join 없이 조회 
List<FieldGroupMapping> fieldGroupMappings1 = fieldGroupRepository.findAllById(idList);

Long gridId = 1L;
// fetch join으로 조회
List<FieldGroupMapping> fieldGroupMappings2 = gridRepository.getGrid(gridId)
    .map(Grid::getFieldGroupMapping)
    .toList();

for (FieldGroupMapping mapping1 : fieldGroupMappings1) {
    FieldMetadata field = mapping1.getField();
    // 여기서 BreakPoint로 field를 보면 Proxy 객체
}

for (FieldGroupMapping mapping2 : fieldGroupMappings2) {
    FieldMetadata field = mapping2.getField()
    // 여기서 BreakPoint로 field를 보면 실제 객체 (Unproxy상태)
}

 

위 상황에서 첫번째 fieldGroupMappings1을 조회할땐 fetch join없이 hibernate로만 조회합니다.

두번째 fieldGroupMappings2를 조회할땐 Grid -> FieldGroupMapping -> field 연관관계 모두 fetch join을 써서 조회합니다.

 

제 지식으로는 두개 for문에서 모두 field는 Unproxy상태여야합니다.
단, fetch join이 없는 첫번째 리스트에서는 Select Query가 나가겠죠.

하지만, 테스트 결과는 첫번째 리스트에서의 field는 계속 Proxy상태로 되어있어. field 내부의 필드들이 null로 되어있었습니다.

 

또, 제 어플리케이션의 설정으로 default batch size는 1000으로 되어있어. select문의 조건에 in절로 채워져서 쿼리가 1번 발생하였습니다.

 

제 지식으로는 Select쿼리가 발생하였으므로, 첫번째 리스트도 field는 실제 객체여야하는데 왜 프록시객체로 남아있을까요? FieldMetadata 엔티티가 상속Entity여서 발생하는 문제일까요?

정답을 알려주세요 ㅠㅠㅠ

 

 

 

 

jpa hibernate

답변 1

0

인프런 AI 인턴

안녕하세요, 인프런 AI 인턴입니다.
papakang22님, 설명하신 문제를 바탕으로 Hibernate에서 발생할 수 있는 프록시 관련 이슈를 살펴보겠습니다.

첫 번째로, fetch join이 없는 경우에는 Hibernate가 연관된 객체를 필요 시점(즉, 실제 데이터 접근 시점)에 lazy loading을 통해 로딩합니다. 이때, FieldMetadata@ManyToOne 관계이므로 기본적으로 LAZY 로딩 전략을 사용합니다. 따라서 mapping1.getField() 호출 시 FieldMetadata의 실제 객체가 아닌 프록시 객체로 반환될 수 있습니다.

반면, 두 번째 케이스에서는 fetch join을 사용하여 쿼리 단계에서 필요한 데이터를 모두 가져오기 때문에 결과적으로 프록시 객체가 아닌 실제 객체로 로드됩니다.

두 경우 간의 차이는 @ManyToOne(fetch = FetchType.EAGER) 설정에 기인할 수 있습니다. EAGER 로딩 설정에도 불구하고 정상적으로 로딩되지 않는 경우가 발생할 수 있으므로, 다음을 확인해 보시는 것이 좋습니다:

  1. EntityManager Configuration: FetchType.EAGER로 설정했음에도 프록시 객체로 반환되는데는 여러 설정 및 환경적 요인이 있을 수 있으니, EntityManager 설정 및 환경을 확인해 보세요.

  2. Inheritance 전략: 상속 구조(@Inheritance(strategy = InheritanceType.JOINED))로 인해 문제 발생 가능성이 있을 수 있습니다. 이런 상황에서는 해당 객체의 타입 캐스팅 및 초기화 과정에서 문제가 발생할 수 있으므로 상속 구조 내의 설정을 확인해 보세요.

  3. Hibernate Initialization 호출: 명시적으로 초기화할 수 있도록 Hibernate.initialize(object)를 통해 프록시 객체를 초기화하는 것도 한 가지 방법입니다.

  4. Hibernate 버그 확인: 사용중인 Hibernate 버전에서 관련된 알려진 버그가 없는지 공식 문서를 통해 확인이 필요합니다.

아래의 Spring Data JPA 공식 문서도 도움이 될 수 있으니 참고 바랍니다:

위 방법으로 해결되지 않는다면, 환경 설정 상태를 좀 더 세부적으로 점검하시거나, 소규모 프로젝트로 테스트해보는 것도 좋은 방법일 수 있습니다. 추가적인 문제가 있으면 다시 질문 주시면 감사하겠습니다.

RepositoryTest의 패키지 위치가 domain인 이유

0

23

2

도메인 모델에서 관계와 규칙을 구분하는 방법

0

35

2

16 강의 메세지 retryCount의 의도

0

21

2

JPA Repository 질문이 있습니다!

1

32

2

페이지네이션 처리를 쿼리에서 하는 방식 질문

1

35

1

UserService, CertificationService 책임 분리 기준 질문

0

23

1

실무 조언 관련 질문입니다.

0

39

1

sdk 설정 오류

0

47

2

H2데이터베이스 파일 생성

0

48

2

SpringBoot 4.X에서의 Querydsl 설정

0

79

2

REQUIRES_NEW 해결 방법에 대해서 질문있습니다!!

0

28

1

오탈자 - @Transactional

0

53

1

Dto와 Entity 사용 관련 질문

0

29

2

서브쿼리 강의에서 ALL 예시 관련 질문드립니다.

0

49

2

EC2 실습 중 docker-compose / docker ps 실행 시 권한 오류 문의

2

70

1

application.properties 작성 관련 질문

0

43

2

색깔구분

0

42

3

gradlew, java -jar 로 애플리케이션 실행시키기에서

1

47

1

리액트 관련 질문이 있습니다.

0

60

2

강의 관련 외 질문입니다.

0

64

2

update()에 사용하는 setter 질문드립니다.

0

46

1

스프링부트 버전

0

66

2

querydsl 오픈소스에 대한 질문

0

69

1

"스프링 부트와 리액트로 구현하는 소셜 로그인" 강의와 차이점이 궁금합니다.

0

48

1