@EqualsAndHashCode 사용시 인텔리제이가 띄우는 경고로그

해당 경고로그는 jpa 사용을 더 쉽게 만들어주는 인텔리제이 플러그인 jpa buddy 가 띄워주는 경고로그입니다.
jpa buddy는 아래의 이유로 위 같은 경고로그를 띄웁니다.
- HashSet | HashMap 의 잘못된 동작
엔티티는 일반적으로 변동 가능성이 있는 클래스입니다. 심지어 id 조차도 변경될 여지가 있습니다.
애플리케이션에서 setter를 생성하지 않는다고 하더라도 DB에 id 생성을 위임한다면 id 조차 변경될 가능성이 있습니다. (영속화 하기 전에는 null → 영속화 한 후에는 특정 숫자값을 가짐)
변경 가능성이 있는 필드로 해시코드를 생성하다 보니 문제가 생길 수 밖에 없습니다.
@Entity @EqualsAndHashCode public class TestEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(nullable = false) private Long id; }
TestEntity는 id를 DB에서 자동 생성하는 전략을 가지고 @EqualsAndHashCode 롬복 어노테이션을 사용하고 있습니다.
@EqualsAndHashCode는 static , transient 가 아닌 모든 필드를 사용합니다.
TestEntity testEntity = new TestEntity(); Set<TestEntity> set = new HashSet<>(); set.add(testEntity); testEntityRepository.save(testEntity); Assert.isTrue(set.contains(testEntity), "Entity not found in the set");
여기서 마지막 테스트 코드는 실패하게 됩니다.
현재 TestEntity에는 static, transient가 아닌 필드는 id 하나뿐이기 때문에 id 필드만을 이용해서 equals(), hashcode() 가 자동생성됩니다.
생성되는 hashcode() 를 까보면 아래와 같습니다.
public int hashCode() { final int PRIME = 59; int result = 1; final Object $id = this.getId(); result = result * PRIME + ($id == null ? 43 : $id.hashCode()); return result; }
set 에 엔티티를 저장할때 해시코드가 생성됩니다. testEntity는 영속화 전에 set에 저장되게 되므로 id가 null인 상태에서 해시코드가 생성됩니다.
그리고 testEntity를 영속화 한 후에 (id가 바뀌었기 때문에 해시코드도 변경됨) set에서 이전과 다른 새롭게 생성된 해시코드로 set에서 객체를 찾으려고 하니 찾지 못하는 문제가 발생합니다.
따라서 UUID로 id가 고정되는 경우가 아니라 DB 생성 id를 사용하는 경우 @EqualsAndHashCode 사용은 문제가 될 수 있습니다.
→ 현재 저희는 DB 생성 id를 사용하고 있기 때문에 영속화 전에 HashSet, HashMap 사용시 문제가 생길 수 있다는것을 팀원 모두가 인지하고 있거나 변동 가능성이 없는 (그런게 있나?) 필드만으로만 해시코드가 생성되도록 해야 할 것 같습니다.
- LazyInitializationException
equals()/hashCode()/toString() 는 기본적으로 전체 필드를 모두 사용합니다
@EqualsAndHashCode 사용시 자동 생성되는 코드 예시
참고:

따라서 jpa를 사용할때 의도치 않은 side effect 가 발생할 수 있습니다.
예를 들어 oneToMany 이고 지연로딩인 필드에 대해 hashcode() 를 호출한다면 모든 엔티티들을 전부 가져오게 되기 때문에 어플리케이션 성능에 문제를 줄 수 있습니다.
그리고 만약 트랜잭션이 없는 상태에서 이런 초기화가 발생한다면 LazyInitializationException 이 발생할 수 있습니다.
그래서 JPA Buddy는 @EqualsAndHashCode 대신 더 안전한 코드로 변경할수 있게 도움을 줍니다.

이때 자동 생성되는 toString() 에서 문제가 발생할 수 있는데,
제외하고자 하는 필드에 @ToString.Exclude를 붙여주거나 @ToString(onlyExplicitlyIncluded=true) 를 클래스에 붙여주고 지연로딩이 아닌 필드에 @ToString.Include 를 붙여주는 식으로 해결할 수 있습니다.

→ 위 내용을 참고해서 코드 변경이 필요할 것 같습니다.