@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public void changeName(){
name = "stir";
this.name = "stir";
}
}
예전에 인프런 질문 답변에 Entity에서 필드 변경을 위해 위와 같이 this로 접근하는 것과 그냥 접근하는 것에 대한 차이를 물어보신 분이 계셨고 김영한님이 친절히 설명해주셨는데 그 글이 어디갔는지 검색해도 안나온다. 깊게 들어가면 프록시에 관련된 답변이었걸로 기억한다.
일단은 예전에 강의 정리한 내용에 의하면 김영한님은 this를 안쓴다고한다.
Intellij에 보라색으로 띄워주기 때문에 그게 더 명료해서라고 하신다.
여튼 그래서 언제 this를 써야할 까에 대해 ChatGpt에 물어봤었는데 나름 명쾌한 답변이 나와서 정리하고자 한다.
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return Objects.equals(name, person.name); // 프록시가 초기화되지 않아 문제 발생 가능
}
// hashCode() 등 다른 메소드들도 적절하게 구현해야 함
}
결과부터 얘기하자면 위의 코드는 문제가 발생하는 코드이며 equals안에 name을 this.name으로 접근해야한다.
프록시는 지연 로딩을 구현하기 위해 사용되는 객체로 실제 엔티티를 감싸면서 엔티티에 대한 데이터베이스 조회를 지연시킨다.
JPA에서는 지연 로딩을 사용하면 로딩되는 동안에 엔티티 객체는 프록시로 감싸져 있으며 해당 객체의 실제 필드가 초기화되지 않은 상태이다.
하지만 this를 쓰지않게되면 쿼리 발생이 일어나는 현상이 나타난다고 한다.
실제로 equals() 메소드를 구현할 때 프록시와 관련하여 this를 꼭 써줘야하는 상황이 이런 상황이다.
EntityManager em = // 엔티티 매니저를 가져오는 코드
// 엔티티 조회 (지연 로딩)
Person person1 = em.find(Person.class, 1L);
Person person2 = em.find(Person.class, 2L);
// equals() 메소드 호출
boolean isEqual = person1.equals(person2);
// 두 Person 객체의 name 필드를 비교하려고 하는데, person2는 프록시 객체이며 프록시 초기화가 발생한다.
좀 더 자세히 설명하면 위와 같은 코드에서 equals가 실행될 때 this로 접근하지 않고 그냥 필드로 바로 접근하면 초기화가 일어난다고 한다.
필드를 직접 접근하면 지연 로딩이 실행되어 프록시 객체가 초기화된다.
반면에 this로 접근하면 단순 멤버 변수 접근이라고만 판단해서 초기화되지 않는다.
즉, 지연로딩 시 equals 비교만 하려고 하는데 쓸 데 없이 쿼리 발생이 일어나는 경우에는 this를 써야한다는 의미다.
'📖 ORM' 카테고리의 다른 글
MSA 환경에서의 JPA 사용법 (0) | 2023.06.21 |
---|---|
Kotlin에서 JPA 사용법 (0) | 2023.06.21 |
Fetch Join 사용 시 조건문(Condition) 올바르게 사용하기 - 실습으로 배우는 JPA 4편 (0) | 2022.11.03 |
@Transactional 사용 시 자기 호출(Self-Invocation) 이슈 - 실습으로 배우는 JPA 3편 (2) | 2022.07.06 |
findAll()에 관한 N+1 테스트 - 실습으로 배우는 JPA 2편 (0) | 2022.06.30 |