Java 实体等于()、hashCode()和toString()。如何正确实施??

Java 实体等于()、hashCode()和toString()。如何正确实施??,java,hibernate,orm,entity,Java,Hibernate,Orm,Entity,我正在使用bean中的所有可用字段实现实体的equals()、hashCode()和toString() 当我尝试比较相等或打印obj状态时,前端出现了一些惰性init异常。这是因为实体中的某些列表可以延迟初始化 我想知道在实体对象上实现equals()和toString()的正确方法是什么。equals()和hashCode()应该使用一个-即一组唯一标识对象但不是其自动生成ID的属性来实现 在toString()中,您可以放置任何感兴趣的信息,例如所有字段 使用IDE(Eclipse、Net

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

当我尝试比较相等或打印obj状态时,前端出现了一些惰性init异常。这是因为实体中的某些列表可以延迟初始化

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

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

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

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


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

当您为Hibernate对象实现equals和hashCode方法时,重要的是

  • 使用getter而不是直接访问类属性
  • 不要直接比较对象的类,而是使用
    instanceof
  • 更多信息:


    编辑:关于不直接访问类属性的相同规则也适用于toString方法-仅使用getter可以保证返回类中真正包含的信息。

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

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

    @Override
    public String toString() {
        return String.format("%s(id=%d)", this.getClass().getSimpleName(), this.getId());
    }
    
    如有必要,my 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;
    }
    
    @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 getClass().hashCode();
        }
    
        //Getters and setters omitted for brevity
    }
    
    @覆盖
    公共布尔等于(对象obj){
    如果(this==obj)返回true;
    if(obj==null)返回false;
    c1类=Hibernate.getClass(此类);
    c2类=Hibernate.getClass(obj);
    如果(!c1.等于(c2))返回false;
    最终AbstractEntity other=(AbstractEntity)obj;
    if(this.getId()==null){
    if(other.getId()!=null)返回false;
    }
    else如果(!this.getId().equals(other.getId())返回false;
    返回true;
    }
    
    正如其他人已经指出的那样。。。访问延迟加载的属性时要小心!如果一个简单的toString()甚至log.debug(实体)级联到几个延迟加载的对象和属性中,可能会导致大量的活动

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

    public boolean equals(Object o) {
        if(o == null)
            return false;
    
       Account account = (Account) o;
       if(!(getId().equals(account.getId())))
           return false;
    
       return true;
    }
    
    但是当您有一个非持久化实体时会发生什么它将不工作,因为尚未分配其标识符

    那么,让我们看看Java持久化与Hibernate的书中讨论了什么

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

    所以

    如果不使用代理主键,则会使用自然键

    因此,假设您有一个用户实体,它的自然键是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;
    }
    
    很好用!我甚至使用模拟对象,如存储库、服务等

    关于toString()方法,正如@Bozho所说,您可以放置任何有趣的信息。但是请记住一些web框架,例如Wicket和Vaadin,使用此方法来显示其值。

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

    等于():
    /**
    *根据hibernate语义比较两个实体对象是否相等。这里我们假设
    *新对象总是不同的,除非它们是相同的对象。如果从中加载对象
    *数据库有一个有效的id,因此我们可以对照对象id进行检查。
    *
    *@see com.dolby.persist.bean.EntityObject#equals(java.lang.Object)
    */
    @抑制警告(“未选中”)
    @凌驾
    公共最终布尔等于(最终对象){
    如果(this==对象)返回true;
    如果(object==null | | this.getClass()!=object.getClass())返回false;
    最终的AbstractModelObject other=(AbstractModelObject)对象;
    if(this.getId()==null | | other.getId()==null)返回false;
    返回此.getId().equals(其他.getId());
    }
    
    hashCode():
    /**
    *返回enttiy对象哈希代码。
    *
    *我们在这里所做的是确保一旦使用了hashcode值,它就永远不会更改
    *这个物体。这使我们能够对新对象使用对象标识,而不会遇到
    *问题。
    *

    * *事实上,这是一个问题的唯一情况是当我们保存一个新对象时,保持它在周围 *关闭会话后,在新会话中加载对象的新实例,然后 *比较一下。 *

    * *在这种情况下,我们得到A==B,但是A.hashcode!=b、 哈希码 *

    * *这将适用于所有其他场景,并且不会在 *对象的属性将被编辑。在中生成合成主键的要点 *首先是避免主键依赖于对象属性和 *因此,在对象的生命周期内可能会发生变化。 *

    * *@见
    /**
     * 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();
    }
    
    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();
    }
    
    @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 getClass().hashCode();
        }
    
        //Getters and setters omitted for brevity
    }