작성
·
21
·
수정됨
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여서 발생하는 문제일까요?
정답을 알려주세요 ㅠㅠㅠ
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
papakang22님, 설명하신 문제를 바탕으로 Hibernate에서 발생할 수 있는 프록시 관련 이슈를 살펴보겠습니다.
첫 번째로, fetch join
이 없는 경우에는 Hibernate가 연관된 객체를 필요 시점(즉, 실제 데이터 접근 시점)에 lazy loading
을 통해 로딩합니다. 이때, FieldMetadata
가 @ManyToOne
관계이므로 기본적으로 LAZY
로딩 전략을 사용합니다. 따라서 mapping1.getField()
호출 시 FieldMetadata
의 실제 객체가 아닌 프록시 객체로 반환될 수 있습니다.
반면, 두 번째 케이스에서는 fetch join
을 사용하여 쿼리 단계에서 필요한 데이터를 모두 가져오기 때문에 결과적으로 프록시 객체가 아닌 실제 객체로 로드됩니다.
두 경우 간의 차이는 @ManyToOne(fetch = FetchType.EAGER)
설정에 기인할 수 있습니다. EAGER
로딩 설정에도 불구하고 정상적으로 로딩되지 않는 경우가 발생할 수 있으므로, 다음을 확인해 보시는 것이 좋습니다:
EntityManager Configuration: FetchType.EAGER
로 설정했음에도 프록시 객체로 반환되는데는 여러 설정 및 환경적 요인이 있을 수 있으니, EntityManager 설정 및 환경을 확인해 보세요.
Inheritance 전략: 상속 구조(@Inheritance(strategy = InheritanceType.JOINED)
)로 인해 문제 발생 가능성이 있을 수 있습니다. 이런 상황에서는 해당 객체의 타입 캐스팅 및 초기화 과정에서 문제가 발생할 수 있으므로 상속 구조 내의 설정을 확인해 보세요.
Hibernate Initialization 호출: 명시적으로 초기화할 수 있도록 Hibernate.initialize(object)
를 통해 프록시 객체를 초기화하는 것도 한 가지 방법입니다.
Hibernate 버그 확인: 사용중인 Hibernate 버전에서 관련된 알려진 버그가 없는지 공식 문서를 통해 확인이 필요합니다.
아래의 Spring Data JPA 공식 문서도 도움이 될 수 있으니 참고 바랍니다:
위 방법으로 해결되지 않는다면, 환경 설정 상태를 좀 더 세부적으로 점검하시거나, 소규모 프로젝트로 테스트해보는 것도 좋은 방법일 수 있습니다. 추가적인 문제가 있으면 다시 질문 주시면 감사하겠습니다.