JPA와 Hibernate를 사용할 때 어떻게 같고 해시 코드를 구현해야 하는가


103

모델 클래스의 같음과 해시 코드는 Hibernate에서 어떻게 구현되어야합니까? 일반적인 함정은 무엇입니까? 대부분의 경우 기본 구현이 충분합니까? 비즈니스 키를 사용하는 것이 어떤 의미가 있습니까?

게으른 가져 오기, ID 생성, 프록시 등을 고려할 때 모든 상황에서 올바르게 작동하는 것이 매우 어려운 것 같습니다.


참조 stackoverflow.com/a/39827962/548473 (스프링 데이터 JPA 구현)
그리고 리 Kislin

답변:


74

Hibernate는 재정의하는 방법을 때 /의 좋은 긴 설명이 equals()/ hashCode()문서

요점은 엔터티가 a의 일부가 Set되거나 인스턴스를 분리 / 연결할 경우에만 걱정할 필요가 있다는 것입니다. 후자는 그다지 일반적이지 않습니다. 전자는 일반적으로 다음을 통해 가장 잘 처리됩니다.

  1. 내놓고 equals()/ hashCode()비즈니스 키 - (적어도, 세션이나) 수명을 객체 동안 변화하지 않을 속성의 예를 들면 독특한 조합.
  2. 위에서 불가능베이스의 경우 equals()/ hashCode()차 키는 IF 그것의 세트와 객체의 정체성 / System.identityHashCode()그렇지. 여기서 중요한 부분은 새 엔티티가 추가되고 지속 된 후에 세트 를 다시로드 해야한다는 것입니다 . 그렇지 않으면 엔티티가 현재 hashCode().

1
@ ChssPly76 "reload"라고하면 refresh()무엇 을하는 것 입니까? Set계약 을 준수하는 엔터티가 어떻게 잘못된 버킷에 들어가게됩니까 (해시 코드 구현이 충분히 좋다고 가정).
non sequitor

4
컬렉션을 새로 고치거나 전체 (소유자) 엔터티를 다시로드합니다. 잘못된 버킷에 관한 한 : a) 설정할 새 엔티티를 추가하면 해당 ID가 아직 설정되지 않았으므로 엔티티를 버킷 # 1에 배치하는 identityHashCode를 사용하고 있습니다. b) 엔티티 (세트 내)가 지속되고 이제 ID가 있으므로 해당 ID를 기반으로 hashCode ()를 사용하고 있습니다. 위와 다르며 엔티티를 버킷 # 2에 배치 했을 것 입니다. 이제 다른 곳에서이 엔티티에 대한 참조를 보유하고 있다고 가정하고 호출을 시도 Set.contains(entity)하면 false. get () / put () / 등도
마찬가지입니다

의미가 있지만, 나는 그것이 자신의 ResultTransformers처럼 최대 절전 모드 소스에 사용되는 참조하지만 identityHashCode에게 자신을 사용한 적이
비 sequitor

1
Hibernate를 사용할 때이 문제가 발생할 수도 있습니다. 아직 해결책을 찾지 못했습니다.
Giovanni Botta 2013

@ ChssPly76 두 개체가 동일한 지 여부를 결정하는 비즈니스 규칙으로 인해 개체 수명 내에서 변경 될 수있는 속성에 대한 등식 / 해시 코드 메서드를 기반으로해야합니다. 정말 큰일인가요? 그렇다면 어떻게해야합니까?
ubiquibacon 2013

39

받아 들여진 대답이 정확하다고 생각하지 않습니다.

원래 질문에 답하려면 :

대부분의 경우 기본 구현이 충분합니까?

대부분의 경우 그렇습니다.

당신은 대체해야 equals()하고 hashcode()엔티티가 사용됩니다 경우 Set(이 매우 일반적이다) 엔티티가 부착 재 이후에서 분리 될 것입니다 (최대 절전 모드의 드문 사용되는) 세션을 최대 절전 모드.

허용되는 대답은 조건 중 하나 가 참이면 메서드를 재정의해야 함을 나타냅니다 .


내 관찰, 시간이 맞 춥니 다 알아낼 이유 .
Vlastimil Ovčáčík

"엔티티가 Set에서 사용될 경우에만 equals () 및 hashcode ()를 재정의해야합니다."는 일부 필드가 객체를 식별하는 경우에 충분하므로 식별하기 위해 Object.equals ()에 의존하고 싶지 않습니다. 사물.
davidxxx

17

가장 좋은 equals/ hashCode당신이 사용할 때 구현은 고유 한 비즈니스 키를 .

비즈니스 키는 모든 엔터티 상태 전환 (일시적, 연결됨, 분리됨, 제거됨)에서 일관성이 있어야합니다. 그렇기 때문에 동일성을 위해 ID에 의존 할 수 없습니다.

또 다른 옵션은 애플리케이션 로직에 의해 할당 된 UUID 식별자 사용으로 전환하는 것 입니다. 이렇게 하면 엔티티가 플러시되기 전에 ID가 할당되기 때문에 equals/에 UUID를 사용할 수 있습니다 hashCode.

equalshashCode에 대해 엔티티 식별자를 사용할 수도 있지만, hashCode모든 엔티티 상태 전환에서 엔티티 hashCode 값이 일관되도록 항상 동일한 값을 반환 해야합니다. 이 주제에 대한 자세한 내용은이 게시물을 확인하십시오 .


uuid 접근 방식의 경우 +1. 그것을 a에 넣고 BaseEntity그 문제에 대해 다시 생각하지 마십시오. 그것은 DB를 측면에 공간이 조금 걸리지 만 그 가격 당신에게 :) 안락을위한 더 나은 급여
마틴 프레이

12

지연로드를 통해 엔티티가로드되면 기본 유형의 인스턴스가 아니지만 javassist에 의해 생성 된 동적으로 생성 된 하위 유형이므로 동일한 클래스 유형에 대한 검사가 실패하므로 다음을 사용하지 마십시오.

if (getClass() != that.getClass()) return false;

대신 다음을 사용하십시오.

if (!(otherObject instanceof Unit)) return false;

이는 Java Practices에서 같음 구현에 설명 된 것처럼 좋은 방법이기도합니다 .

같은 이유로 필드에 직접 액세스하면 기본 값 대신 작동하지 않고 null을 반환 할 수 있으므로 속성에 대한 비교를 사용하지 말고 getter를 사용하여 기본 값을로드하도록 트리거 할 수 있습니다.


1
이것은 내 상황에서 작동하지 않는 구체적인 클래스의 객체를 비교하는 경우 작동합니다. 나는 슈퍼 클래스의 객체를 비교하고 있었는데,이 경우이 코드가 저에게 효과적이었습니다. obj1.getClass (). isInstance (obj2)
Tad

6

예, 어렵습니다. 내 프로젝트에서 equals와 hashCode는 둘 다 객체의 ID에 의존합니다. 이 솔루션의 문제점은 ID가 데이터베이스에 의해 생성되기 때문에 오브젝트가 아직 지속되지 않은 경우 둘 다 작동하지 않는다는 것입니다. 제 경우에는 거의 모든 경우에 객체가 즉시 유지되기 때문에 허용됩니다. 그 외에는 훌륭하게 작동하고 구현하기 쉽습니다.


ID가 생성되지 않은 경우 객체 ID를 사용하는 것으로 생각합니다
Kathy Van Stone

2
여기서 문제는 객체를 유지하면 해시 코드가 변경된다는 것입니다. 객체가 이미 해시 기반 데이터 구조의 일부인 경우 큰 해로운 결과를 초래할 수 있습니다. 따라서 개체 ID를 사용하는 경우 개체가 완전히 해제 될 때까지 obj id를 계속 사용하는 것이 좋습니다 (또는 해시 기반 구조에서 개체를 제거하고 유지 한 다음 다시 추가). 개인적으로 나는 id를 사용하지 않는 것이 가장 좋을 것이라고 생각하고 객체의 불변 속성에 해시를 기반으로합니다.
Kevin Day

1

Hibernate 5.2의 문서에서는 상황에 따라 hashCode 및 equals를 구현하고 싶지 않을 수도 있다고 말합니다.

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

일반적으로 동일한 세션에서로드 된 두 개체는 데이터베이스에서 동일하면 동일합니다 (hashCode 및 equals를 구현하지 않음).

두 개 이상의 세션을 사용하면 복잡해집니다. 이 경우 두 객체의 동등성은 동등 메소드 구현에 따라 다릅니다.

또한 equals-method가 처음으로 객체를 지속하는 동안 만 생성 된 ID를 비교하는 경우 문제가 발생할 수 있습니다. equals가 호출 될 때 아직 거기에 없을 수도 있습니다.


0

여기에 아주 좋은 기사가 있습니다 : https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

기사에서 중요한 내용을 인용 :

비즈니스 키 동등성을 사용하여 equals () 및 hashCode ()를 구현하는 것이 좋습니다. 비즈니스 키 동등성은 equals () 메서드가 실제 세계에서 인스턴스를 식별하는 키 (자연 후보 키) 인 비즈니스 키를 형성하는 속성 만 비교 함을 의미합니다.

간단히 말해서

public class Cat {

...
public boolean equals(Object other) {
    //Basic test / class cast
    return this.catId==other.catId;
}

public int hashCode() {
    int result;

    return 3*this.catId; //any primenumber 
}

}

0

을 재정의 한 경우 equals계약을 이행했는지 확인하십시오.

  • 대칭
  • 반사
  • 전이
  • 일관된
  • NULL이 아님

hashCode계약은 equals구현에 의존하므로 재정의하십시오 .

Joshua Bloch (컬렉션 프레임 워크 설계자)는 이러한 규칙을 준수 할 것을 강력하게 촉구했습니다.

  • 항목 9 : equals를 재정의 할 때 항상 hashCode를 재정의합니다.

이러한 계약을 따르지 않으면 의도하지 않은 심각한 결과가 발생합니다. 예를 들어 일반 계약이 이행되지 않아 List#contains(Object o)잘못된 boolean값을 반환 할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.