Java 实体 equals()、hashCode() 和 toString()。如何正确实施它们?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2446590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Entities equals(), hashCode() and toString(). How to correctly implement them?
提问by spike07
I'm implementing equals()
, hashCode()
and toString()
of my entities using all the available fields in the bean.
我正在使用 bean 中的所有可用字段实现equals()
,hashCode()
和toString()
我的实体。
I'm getting some Lazy init Exception on the frontend when I try to compare the equality or when I print the obj state. That's because some list in the entity can be lazy initialized.
当我尝试比较相等性或打印 obj 状态时,我在前端遇到了一些 Lazy init Exception。那是因为实体中的某些列表可以被延迟初始化。
I'm wondering what's the correct way to for implementing equals()
and toString()
on an entity object.
我想知道在实体对象上实现equals()
和执行的正确方法是什么toString()
。
回答by Bozho
equals()
and hashCode()
should be implemented using a business key- i.e. a set of properties that uniquely identify the object, but are not its auto-generated ID.
equals()
并且hashCode()
应该使用业务键来实现- 即一组唯一标识对象的属性,但不是其自动生成的 ID。
in toString()
you can put whatever information is interesting - for example all fields.
在toString()
你可以把任何信息是有趣的-例如所有字段。
Use your IDE (Eclipse, NetBeans, IntelliJ) to generate all these for you.
使用您的 IDE(Eclipse、NetBeans、IntelliJ)为您生成所有这些。
In order to avoid LazyInitializationException
, no matter whether in equals()
or in your view (jsp), you can use OpenSessionInView
.
为了避免LazyInitializationException
,无论是在equals()
或在您的视图(JSP),你可以使用OpenSessionInView
。
回答by simon
When you implement the equals and hashCode methods for Hibernate objects, it is important to
当你为 Hibernate 对象实现 equals 和 hashCode 方法时,重要的是
- Use getters instead of directly accessing the class properties.
- Not directly compare objects' classes, but use
instanceof
instead
- 使用 getter 而不是直接访问类属性。
- 不能直接比较对象的类,但使用
instanceof
替代
More information:
更多信息:
Stackoverflow: overriding-equals-and-hashcode-in-java
Stackoverflow:覆盖-equals-and-hashcode-in-java
Hibernate documentation: Equals and HashCode
Hibernate 文档:Equals 和 HashCode
Edit: the same rules about not accessing the class properties directly applies to toString method as well - only using the getters guarantees that the information really contained in the class is returned.
编辑:关于不直接访问类属性的相同规则也适用于 toString 方法 - 只有使用 getter 才能保证返回真正包含在类中的信息。
回答by extraneon
Apart from what the others said I also think a Hibernate object needs to still be attached to the session to retrieve lazy information. Without a database connection those lists can not be loaded :)
除了其他人所说的,我还认为一个 Hibernate 对象仍然需要附加到会话以检索惰性信息。如果没有数据库连接,则无法加载这些列表:)
回答by Daniel Bleisteiner
My implementation of toString() für Hibernate entities is the following:
我对 Hibernate 实体的 toString() 实现如下:
@Override
public String toString() {
return String.format("%s(id=%d)", this.getClass().getSimpleName(), this.getId());
}
Every subclass of my AbstractEntity (above) overrides that method if necessary:
如有必要,我的 AbstractEntity(以上)的每个子类都会覆盖该方法:
@Override
public String toString() {
return String.format("%s(id=%d, name='%s', status=%s)",
this.getClass().getSimpleName(),
this.getId(),
this.getName(),
this.getStatus());
}
For hashCode() and equals() keep in mind that Hibernate often uses proxy classes. So my equals() usally looks like this:
对于 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;
}
And as others already stated... be careful with accessing lazy loaded properties! A simple toString() or even log.debug(entity) might cause huge activity if cascading into several lazy loaded objects and properties.
正如其他人已经说过的那样......访问延迟加载的属性时要小心!如果级联到几个延迟加载的对象和属性中,一个简单的 toString() 甚至 log.debug(entity) 可能会导致巨大的活动。
回答by Arthur Ronald
- If two objects are equal, they must havethe same hashcode.
- equals() method, by default, checks whether two references refer to the same in-memory instance on the Java heap
- 如果两个对象相等,则它们必须具有相同的哈希码。
- 默认情况下,equals() 方法会检查两个引用是否指向 Java 堆上的同一个内存中实例
You can rely on Entity identifierto compare your Entity by using equals
您可以依靠实体标识符来使用 equals 比较您的实体
public boolean equals(Object o) {
if(o == null)
return false;
Account account = (Account) o;
if(!(getId().equals(account.getId())))
return false;
return true;
}
Butwhat happens when you have a non-persisted Entity. It will not workbecause its Identifier has not been assigned.
但是当你有一个非持久化实体时会发生什么。它不会工作,因为它的标识符尚未分配。
So Let's see what Java Persistence with Hibernate Book talks about it
那么让我们看看 Java Persistence with Hibernate Book 是怎么说的
A business key is a property, or some combination of properties, that is unique for each instance with the same database identity.
业务键是一个属性或一些属性组合,对于具有相同数据库标识的每个实例都是唯一的。
So
所以
It is the natural key that you would useif you weren't using a surrogate primary key instead.
如果您不使用代理主键,则它是您将使用的自然键。
So let's suppose you have a User Entity and its natural keys are firstName and lastName (At least, his/her firstName and lastName often does not change). So it would be implemented as
所以让我们假设你有一个用户实体,它的自然键是 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;
}
It works fine! I use even with Mock objects like repositories, services etc
它工作正常!我什至使用 Mock 对象,如存储库、服务等
And about toString() method, as said by @Bozho, you can put whatever information is interesting. But remember some web frameworks, like Wicket and Vaadin, for instance, use this method to show its values.
关于 toString() 方法,正如@Bozho 所说,您可以放入任何有趣的信息。但是请记住一些 Web 框架,例如 Wicket 和 Vaadin,使用这种方法来显示其值。
回答by Kango_V
We implement equals() and hashCode() in our super class. This has work flawlessly, especially in Maps and Lists etc. This had to right as we do a lot transitive persistence.
我们在超类中实现了 equals() 和 hashCode()。这可以完美地工作,尤其是在 Maps 和 Lists 等中。这必须正确,因为我们做了很多传递持久性。
equals():
等于():
/**
* 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();
}
回答by doofus
This is probably the best and most straightforward way to do it:
这可能是最好和最直接的方法:
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();
}
回答by Awan Biru
If you happened to override equals() on Hibernate entities, make sure you fulfill its contracts:-
如果您碰巧在 Hibernate 实体上覆盖了 equals(),请确保您履行其合同:-
- SYMMETRY
- REFLECTIVE
- TRANSITIVE
- CONSISTENT
- NON NULL
- 对称
- 反光的
- 传递性
- 持续的
- 非空
And override hashCode
, as its contract rely on equals
implementation.
和 override hashCode
,因为它的合约依赖于equals
实现。
Joshua Bloch(designer of Collection framework) strongly this rule to be followed
Joshua Bloch(Collection 框架的设计者)强烈要遵循这个规则
- item 9: Always override hashCode when you override equals
- 第 9 项:在覆盖 equals 时始终覆盖 hashCode
There are serious unintended effect when you don't follow its contract. For example List.contains(Object o)
might return wrong boolean
value as the general contract not fulfilled.
当您不遵守合同时,会产生严重的意外影响。例如,由于未履行一般合同,List.contains(Object o)
可能会返回错误的boolean
值。
回答by Vlad Mihalcea
- If you have a business key, then you should use that for
equals
/hashCode
. - If you don't have a business key, you should not leave it with the default
Object
equals and hashCode implementations because that does not work after youmerge
and entity. You can use the entity identifier as suggested in this post. The only catch is that you need to use a
hashCode
implementation that always returns the same value, like this:@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 }
- 如果您有业务密钥,那么您应该将其用于
equals
/hashCode
。 - 如果您没有业务密钥,则不应将其保留为默认的
Object
equals 和 hashCode 实现,因为这在您merge
和实体之后不起作用。 您可以使用本文中建议的实体标识符。唯一的问题是您需要使用
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 }