본문 바로가기
TIL

[231201] JPA와 영속성 컨텍스트

by 진진리 2023. 12. 1.
728x90
지난 과제를 어느 정도 마무리하면서 추가로 궁금했던 내용들을 정리해보려고 한다.
JPA에서는 엔티티 매니저에 의해 엔티티들을 영속화시켜 주는데, Spirng data JPA의 경우 내부적으로 처리해주다보니 오히려 영속화나 트랜잭션 과정에 대해 이해가 잘 되지 않았다.
그래서 영속성 컨텍스트에 대해 더 공부할 필요성을 느꼈다.

 

Spring Security 필터에서 받아온 User 엔티티는 영속화되어 있지 않다

  • 엔티티의 변화가 일어나는 곳은 반드시 트랜잭션이 필요하다
    • DB 상에 엔티티 데이터의 변화를 반영하기 위해서 !
  • 스프링 시큐리티 컨텍스트에서의 JPA 엔티티는 준영속 상태
    • 스프링 시큐리티 컨텍스트 principal에는 해당 엔티티를 구분할 수 있는 ID 정도의 문자열만 저장하자
  • 따라서 @AuthenticationPrincipal에서 가져온 User를 조회할 때 연관 관계에 있는 엔티티가 지연 로딩으로 설정되어 있는 경우LazyInitializationException이 발생 !
    • 해당 오류가 지난 TIL에서 정리한 내용이다.

 

프록시 객체와 entity equals

  • 영속화 문제 외에도 필터로 받아온 user 엔티티와 게시글과 연관관계에 있는 user를 equals로 비교할 때 실제 값은 동일함에도 불구하고 왜 false가 반환되었는지 궁금해서 찾아보았다.
  • Object의 equals 메소드: 주소값 비교
public boolean equals(Object obj) {
    return (this == obj);
}
  • 인텔리제이의 자동 생성 기능을 사용한 User에서 오버라이딩한 equals 메소드
    • hibernate의 프록시 객체와 함께 사용되었을 때 문제 발생 !
      1. 프록시 객체 user에서 equals 호출 -> 실제 객체를 초기화(지연로딩)
      2. 실제 객체의 equals 호출 -> 실제 객체의 equals로 프록시 객체가 전달
      3. getClass() != o.getClass() 부분에서 실제 객체와 프록시 객체를 비교
      4. false 반환
@Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        User user = (User) o;
        return Objects.equals(userId, user.userId) && Objects.equals(username,
            user.username) && Objects.equals(password, user.password) && role == user.role;
    }
  • 해결 방법
    • !(o instanceof User) 사용
      • 실제 객체를 상속받은 프록시 객체가 들어와도 false를 반환하지 않음
      • 하지만 프록시 객체 외에 User를 상속한 모든 타입에 대해 id가 같으면 true를 반환
    • Hibernate.getClass(this) != Hibernate.getClass(o) 사용
      • Hibernate.getClass: 프록시 타입이면 실제 객체의 클래스 타입을 반환
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) {
            return false;
        }
        User user = (User) o;
        return Objects.equals(userId, user.userId) && Objects.equals(username,
            user.username) && Objects.equals(password, user.password) && role == user.role;
    }

 

참고: https://ksh-dev.tistory.com/78

 

jpa hibernate 프록시와 entity equals

사실이 아니라 공부한 내용과 생각을 정리한 글입니다. 언제든 가르침을 주신다면 감사하겠습니다. jpa hibernate 지연로딩과 프락시 지연로딩의 필요성: 연관된 엔티티가 항상 필요하지 않을 수

ksh-dev.tistory.com

 

 

영속성 컨텍스트의 생존 범위와 트랜잭션의 범위가 같다

프록시 객체를 초기화하기 위해서는 영속성 컨텍스트가 필요하다고 알게 되었는데,
팀 과제를 수정하는 과정에서 프록시 객체를 초기화하기 위해 레포지토리에서 조회하여 객체를 반환받았다.
단순 조회할 때에도 영속성 컨텍스트가 유지될까? 라는 의문이 들었다.
  • 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용
    • 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 영속성 컨텍스트를 종료한다.
  • 트랜잭션 범위 밖의 영속성 컨텍스트는 보통 단순 조회성으로 사용하고, 지연로딩, 더티 체킹이 안된다.
  • 프록시 객체를 레포지토리에서 조회함으로써 영속성 컨텍스트에서 초기화 가능하다.