Entity 의 Equals 와 HashCode 를 오버라이드 해도 될까?
엔티티 검증
테스트 코드를 작성하다가, 두 객체가 같은지 비교하는 로직의 작성이 필요했습니다.
@Test
void TestSomething() {
// ... 생략
assertThat(resultEntity.getId()).isEqualTo(expectedEntity.getId());
assertThat(resultEntity.getName()).isEqualTo(expectedEntity.getName());
assertThat(resultEntity.getPrice()).isEqualTo(expectedEntity.getPrice());
assertThat(resultEntity.getStockQuantity()).isEqualTo(expectedEntity.getStockQuantity());
}
이렇게 모든 속성을 비교하는 로직을 작성하는게 번거로웠습니다.
그래서 Equals
와 HashCode
를 오버라이드 했는데요.
여기서 문득 Entity
의 Equals
와 HashCode
하면 문제가 없을까? 에 대해 생각해보게 되었습니다.
궁금했던 내용들
JPA
의영속성 컨텍스트
의 동작에 영향을 주지 않을까?- 만약 영향을 준다면, 영속된 객체와 그렇지 않은 객체의 구분이 안될까?
- 두
Instance
가 같은Entity
인지를 판단하는 기준은 무엇일까?
일단 이 고민을 시작하게된 프로젝트는 Spring Data JDBC(Data JDBC)
를 사용중이었습니다.Data JDBC
는 어차피 Entity
를 즉시 쿼리로 바꿔서 날리기 때문에 Equals
와 HashCode
는 걱정하지 않아도 되겠다고 생각했습니다.
하지만 영속성 컨텍스트
를 가지는 JPA
에서는 어떻게 될지 궁금했습니다.
그리고 더 근본적인 궁금증으로, Entity
자체의 의미와 같은 Entity
를 판단하는 기준이 궁금했습니다.
1. JPA
의 영속성 컨텍스트
의 동작에 영향을 주지 않을까?
백문이 불여일견, 바로 코드로 확인해봤습니다.
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Users {
@Id
private Long id;
private String name;
}
public class UsersJpaTest {
private EntityManagerFactory emf = Persistence.createEntityManagerFactory("hashcode-jpa-test");
private EntityManager em = emf.createEntityManager();
private EntityTransaction tx = em.getTransaction();
@Test
void test() {
Users users = new Users(1L, "jwkim");
tx.begin();
em.persist(users);
Users findUser = em.find(Users.class, users.getId());
tx.commit();
assertThat(em.contains(users)).isTrue();
assertThat(em.contains(findUser)).isTrue();
assertThat(em.contains(new Users(1L, "jwkim"))).isFalse();
}
}
먼저, Equals
와 HashCode
를 오버라이드 하지 않은 Entity
로 테스트를 해봤습니다.
이 테스트는 예상했던 대로, 성공했습니다.
그러면 이제 Users
의 Equals
와 HashCode
를 오버라이드 해보고 다시 테스트 해보겠습니다.
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode // `Equals` 와 `HashCode` 를 오버라이드하는 Lombock 어노테이션 추가
public class Users {
@Id
private Long id;
private String name;
}
오버라이드 한 다음의 테스트도 성공했습니다.
이번 테스트로 영속성 컨텍스트
는 Equals
와 HashCode
에 무관하게 동작하니 기술적으로는 안심하고 사용할 수 있을 것 같습니다.
2. 두 Instance
가 같은 Entity
인지를 판단하는 기준은 무엇일까?
Eric Evans 의 Domain-Driven Design
에서는 Entity
에 대해 다음과 같이 정의합니다.
Many objects are not fundamentally defined by their attributes, but rather by a thread of continuity and identity.
여기서 핵심적인 부분만 해석해보면 객체들은 연속성과 정체성에 의해 정의된다 라고 생각할 수 있었습니다.
다음으로 Martin Fowler
언급한 Entity
에 대해 살펴보았습니다.(EvansClassification)
Entity: Objects that have a distinct identity that runs through time and different representations. You also hear these called "reference objects".
Martin Fowler
도 Domain-Driven Design
을 언급하며 위와 같은 정의에 동의한다고 하는데요.
이렇게 Entity
의 정의에 대해 찾아보며 들었던 생각은, 같은 Entity
인지 확인하는데는 속성
은 중요하지 않다 입니다.
오히려 identity
가 중요하다고 하니 Entity
를 나타내는 고유 식별자만 같다면 같은 Entity
라고 정리할 수 있습니다.
JPA Buddy
JPA 개발을 편하게 할 수 있도록 도와주는 JPA Buddy 라는 IntelliJ 플러그인이 있습니다.
이 플러그인에서 자동으로 생성한 Equals
와 HashCode
는 아래와 같은 모습이라고 합니다.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o))
return false;
Item item = (Item) o;
return Objects.equals(id, item.id);
}
@Override
public int hashCode() {
return id.intValue();
}
여기서 hashCode()
메소드를 보면, id
를 이 객체의 해시값으로 사용하겠다고 되어있습니다.
즉, 속성
이 달라도 id
만 같으면 같은 Entity
라는 개념이 적용된 것 입니다.
결론
JPA
의영속성 컨텍스트
의 동작에 영향을 주지 않을까?- 결론 : 영향을 주지 않는다
- 만약 영향을 준다면, 영속된 객체와 그렇지 않은 객체의 구분이 안될까?
- 결론 : 영향을 주지 않는다
- 두
Instance
가 같은Entity
인지를 판단하는 기준은 무엇일까?Entity
의식별자
가 같으면, 같은Entity
로 판단한다.
결론적으로 Entity
는 Equals
와 HashCode
를 오버라이드 해도 상관이 없다.
다만 id
가 같으면 같은 Entity
로 취급하는게 정론인 것 같아서, 만약 오버라이드 할때는 이 개념을 유의하자.