JPA 프록시, 리플렉션

오젼·2024년 7월 29일
0

❗️jpa에선 프록시, 리플렉션을 위해 기본 생성자가 필요함!!

else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
       method.getDeclaringClass().isAssignableFrom(Advised.class)) {
    // Service invocations on ProxyConfig with the proxy config...
    return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

자꾸 500 에러가 났는데 디버깅 해보니까 저기로 들어갔음...

문제는 내가 기본생성자를 정의하지 않았던 것..!

@NoArgsConstructor(access = AccessLevel.PROTECTED) // 원랜 없었음. 그래서 문제가 생김.
public class Diary extends BaseEntity {
    ...
    @Builder(access = AccessLevel.PRIVATE)
    public Diary(User user, String title, String content, LocalDate diaryDate, Image image, Comment comment) { // 명시적 생성자가 있어서 spring boot에서 자동으로 기본 생성자 안 만들어줌.
        this.user = user;
        this.title = title;
        this.content = content;
        this.diaryDate = diaryDate;
        this.image = image;
        this.comment = comment;
    }

    public static Diary create(User user, String title, String content, LocalDate diaryDate) {
        return Diary.builder()
                .user(user)
                .title(title)
                .content(content)
                .diaryDate(diaryDate)
                .build();
    }
    ...
}

원래는 기본생성자 없어도 스프링 부트에서 자동으로 생성해주는데
명시적 생성자가 있는 경우는 자동으로 생성을 안 해준다고 한다.. 내 경우가 그것...

@NoArgsConstructor(access = AccessLevel.PROTECTED)
추가해주니까 해결됨..
엔티티에 생성자 제대로 만들었는지 꼭!!꼭!!!!!!!! 확인하기

프록시

프록시는 실제 객체를 대신하는 대리 객체입니다. JPA와 Hibernate에서 주로 다음 목적으로 프록시를 사용합니다:

a) 지연 로딩 (Lazy Loading):

  • 연관된 엔티티를 실제로 사용할 때까지 데이터베이스에서 로딩을 지연시킵니다.
  • 예: @ManyToOne(fetch = FetchType.LAZY)로 설정된 관계에서 사용됩니다.

b) 성능 최적화:

  • 필요한 시점에만 실제 객체를 로딩하여 메모리 사용과 데이터베이스 쿼리를 최적화합니다.

c) 트랜잭션 관리:

  • 프록시를 통해 트랜잭션 경계를 관리하고 필요한 시점에 데이터베이스 연산을 수행합니다.

프록시 생성 과정

  1. Hibernate는 엔티티 클래스를 상속받는 프록시 클래스를 동적으로 생성합니다.
  2. 이 프록시 클래스는 원본 엔티티의 메서드를 오버라이드하여 지연 로딩 등의 기능을 구현합니다.
  3. 프록시 객체가 생성될 때 기본 생성자를 사용하여 인스턴스화됩니다.

리플렉션 사용

리플렉션은 실행 중인 자바 프로그램이 자체적으로 검사하거나 클래스의 내부 속성을 조작할 수 있게 해주는 API입니다. JPA와 Hibernate에서 리플렉션을 사용하는 주요 이유는 다음과 같습니다:

a) 동적 객체 생성:

  • 런타임에 클래스 이름을 사용하여 객체를 생성할 수 있습니다.
  • 이는 JPA가 데이터베이스 레코드를 해당 엔티티 객체로 변환할 때 유용합니다.

b) 프로퍼티 접근:

  • private 필드에 접근하거나 수정할 수 있습니다.
  • 이를 통해 JPA는 엔티티의 상태를 관리하고 변경을 감지할 수 있습니다.

c) 메타데이터 분석:

  • 어노테이션을 읽고 처리할 수 있습니다.
  • 예: @Entity, @Column 등의 JPA 어노테이션을 분석하여 매핑 정보를 구성합니다.

리플렉션 사용 예시

  1. 엔티티 인스턴스화:

    Constructor<?> ctor = entityClass.getDeclaredConstructor();
    ctor.setAccessible(true);
    Object entity = ctor.newInstance();
  2. 프로퍼티 설정:

    Field field = entityClass.getDeclaredField("id");
    field.setAccessible(true);
    field.set(entity, 1L);
  3. 메서드 호출:

    Method method = entityClass.getDeclaredMethod("setName", String.class);
    method.setAccessible(true);
    method.invoke(entity, "John Doe");

프록시 생성과 리플렉션 사용은 JPA와 Hibernate가 유연하고 강력한 ORM 기능을 제공할 수 있게 해주는 핵심 메커니즘입니다. 그러나 이러한 기능을 사용하려면 기본 생성자가 필요하며, 이는 우리가 엔티티 클래스를 설계할 때 고려해야 할 중요한 점입니다.

0개의 댓글