实体等于(),hashCode()和toString()。如何正确实施它们?

时间:2010-03-15 11:05:49

标签: java hibernate orm entity

我正在使用bean中的所有可用字段实现我的实体的equals()hashCode()toString()

当我尝试比较相等或者打印obj状态时,我在前端得到了一些Lazy init Exception。那是因为实体中的某些列表可以延迟初始化。

我想知道在实体对象上实现equals()toString()的正确方法是什么。

9 个答案:

答案 0 :(得分:17)

equals()hashCode()应使用business key实现 - 即一组唯一标识对象的属性,但不是其自动生成的ID。

toString()中,您可以放置​​任何有趣的信息 - 例如所有字段。

使用您的IDE(Eclipse,NetBeans,IntelliJ)为您生成所有这些。

为了避免LazyInitializationException,无论是在equals()还是在您的视图(jsp)中,您都可以使用OpenSessionInView

答案 1 :(得分:10)

为Hibernate对象实现equals和hashCode方法时,

非常重要
  1. 使用getter而不是直接访问类属性。
  2. 不直接比较对象的类,而是使用instanceof代替
  3. 更多信息:

    Stackoverflow: overriding-equals-and-hashcode-in-java

    Hibernate documentation: Equals and HashCode

    编辑:关于不访问类属性的相同规则也直接应用于toString方法 - 仅使用getters保证返回实际包含在类中的信息。

答案 2 :(得分:7)

  1. 如果两个对象相等,则必须具有相同的哈希码。
  2. equals()方法,默认情况下,检查两个引用是否引用Java堆上的相同内存中实例
  3. 可以依靠实体标识符来使用等于

    来比较您的实体
    public boolean equals(Object o) {
        if(o == null)
            return false;
    
       Account account = (Account) o;
       if(!(getId().equals(account.getId())))
           return false;
    
       return true;
    }
    

    但是当你有一个非持久的实体时会发生什么。 无法使用,因为尚未分配其标识符。

    让我们看看Java Persistence with Hibernate Book对它的讨论

      

    业务键是属性或属性的某种组合,对于具有相同数据库标识的每个实例都是唯一的

    所以

      

    如果您没有使用代理主键,则使用是自然键。

    因此,假设您有一个用户实体,其自然键是firstName和lastName(至少,他/她的firstName和lastName通常不会更改)。所以它将实现为

    public boolean equals(Object o) {
        if(o == null)
            return false;
    
        if(!(o instanceof User))
            return false;
    
        // Our natural key has not been filled
        // So we must return false;
        if(getFirstName() == null && getLastName() == null)
            return false;
    
        User user = (User) o;
        if(!(getFirstName().equals(user.getFirstName())))
            return false;
    
        if(!(getLastName().equals(user.getLastName())))
            return false;
    
       return true;
    }
    
    // default implementation provided by NetBeans
    public int hashcode() {
        int hash = 3;
    
        hash = 47 * hash + ((getFirstName() != null) ? getFirstName().hashcode() : 0)
        hash = 47 * hash + ((getLastName() != null) ? getLastName().hashcode() : 0)
    
        retrun hash;
    }
    

    工作正常!我甚至使用Mock对象,如存储库,服务等

    关于toString()方法,正如@Bozho所说,你可以把任何有趣的信息都放进去。但是请记住一些Web框架,比如Wicket和Vaadin,使用这种方法来显示它的值。

答案 3 :(得分:0)

除了其他人所说的,我还认为Hibernate对象仍需要附加到会话以检索惰性信息。如果没有数据库连接,则无法加载这些列表:)

答案 4 :(得分:0)

我对toString()fürHibernate实体的实现如下:

@Override
public String toString() {
    return String.format("%s(id=%d)", this.getClass().getSimpleName(), this.getId());
}

如果需要,我的AbstractEntity(上面)的每个子类都会覆盖该方法:

@Override
public String toString() {
    return String.format("%s(id=%d, name='%s', status=%s)",
            this.getClass().getSimpleName(),
            this.getId(),
            this.getName(),
            this.getStatus());
}

对于hashCode()和equals(),请记住Hibernate经常使用代理类。所以我的equals()通常看起来像这样:

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;

    Class<AbstractEntity> c1 = Hibernate.getClass(this);
    Class<AbstractEntity> c2 = Hibernate.getClass(obj);
    if (!c1.equals(c2)) return false;

    final AbstractEntity other = (AbstractEntity) obj;
    if (this.getId() == null) {
        if (other.getId() != null) return false;
    }
    else if (!this.getId().equals(other.getId())) return false;

    return true;
}

正如其他人已经说过的那样......小心访问延迟加载的属性!如果级联到多个延迟加载的对象和属性中,简单的toString()或甚至log.debug(实体)可能会导致巨大的活动。

答案 5 :(得分:0)

我们在超类中实现了equals()和hashCode()。这项工作完美无瑕,特别是在地图和列表等方面。 这必须正确,因为我们做了很多传递性的持久性。

等于():

/**
 * Compare two entity objects, following hibernate semantics for equality. Here we assume that
 * new objects are always different unless they are the same object. If an object is loaded from
 * the database it has a valid id and therefore we can check against object ids.
 *
 * @see com.dolby.persist.bean.EntityObject#equals(java.lang.Object)
 */
@SuppressWarnings("unchecked")
@Override
public final boolean equals(final Object object) {
    if (this == object) return true;
    if (object == null || this.getClass() != object.getClass()) return false;
    final AbstractModelObject<ID> other = (AbstractModelObject<ID>) object;
    if (this.getId() == null || other.getId() == null) return false;
    return this.getId().equals(other.getId());
}

hashCode()方法:

/**
 * Returns an enttiy objects hashcode.
 * <p>
 * What we are doing here is ensuring that once a hashcode value is used, it never changes for
 * this object. This allows us to use object identity for new objects and not run into the
 * problems.
 * </p>
 * <p>
 * In fact the only case where this is a problem is when we save a new object, keep it around
 * after we close the session, load a new instance of the object in a new session and then
 * compare them.
 * </p>
 * <p>
 * in this case we get A==B but a.hashcode != b.hashcode
 * </p>
 * <p>
 * This will work in all other scenarios and don't lead to broken implementations when the
 * propety of the object are edited. The whole point in generating synthetic primary keys in the
 * first place is to avoid having a primary key which is dependant on an object property and
 * which therefore may change during the life time of the object.
 * </p>
 *
 * @see java.lang.Object#hashCode()
 */
@Override
public final synchronized int hashCode() {
    if (this.hashcodeValue == null) {
        if (this.getId() == null) {
            this.hashcodeValue = new Integer(super.hashCode());
        }
        else {
            final int generateHashCode = this.generateHashCode(this.getId());
            this.hashcodeValue = new Integer(generateHashCode);
        }
    }
    return this.hashcodeValue.intValue();
}

答案 6 :(得分:0)

这可能是最好和最直接的方法:

public String toString() {
    return "userId: " + this.userId + ", firstName: " + this.firstName + ", lastName: " + this.lastName + ", dir: " + this.dir + ", unit: " + this.unit + ", contractExpiryDate: " + this.contractExpiryDate + ", email: " + this.email + ", employeeNumber: " + this.employeeNumber + ", employeeType: " + this.employeeType + ", phone: " + this.phone + ", officeName: " + this.officeName + ", title: " + this.title + ", userType: " + this.userType;
}

public boolean equals(Object obj) {
[...]
return (toString().equals(other.toString()));
}

public int hashCode() {
return toString().hashCode();
}

答案 7 :(得分:0)

如果碰巧在Hibernate实体上覆盖了equals(),请确保履行合同: -

  • SYMMETRY
  • REFLECTIVE
  • 传递性
  • 一致
  • NON NULL

并覆盖hashCode,因为其合同依赖于equals实施。

Joshua Bloch(Collection框架的设计者)强烈遵循这条规则

  • 第9项:覆盖等于
  • 时始终覆盖hashCode

如果不遵守合同,会产生严重的意外影响。例如,List.contains(Object o)可能会返回错误的boolean值,因为未达到一般合同。

答案 8 :(得分:0)

  1. 如果您有business key,则应将其用于equals / hashCode
  2. 如果您没有业务密钥,则不应将其保留为默认的Object equals和hashCode实现,因为在merge和实体之后这不起作用。
  3. 您可以use the entity identifier as suggested in this post。唯一的问题是您需要使用始终返回相同值的hashCode实现,如下所示:

    @Entity
    public class Book implements Identifiable<Long> {
    
        @Id
        @GeneratedValue
        private Long id;
    
        private String title;
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Book)) return false;
            Book book = (Book) o;
            return getId() != null && 
               Objects.equals(getId(), book.getId());
        }
    
        @Override
        public int hashCode() {
            return 31;
        }
    
        //Getters and setters omitted for brevity
    }