TIL

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

진진리 2023. 12. 1. 16:20
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

 

 

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

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