Should @Transient property be used in equals/hashCode/toString?

耗尽温柔 提交于 2019-12-20 13:01:06

问题


I have JPA entities where some properties are annotated with @Transient.

Should I use these properties in equals/hashCode/toString methods?

My first thought is NO but I don't know why.

  • Tips?
  • Ideas?
  • Explanations?

回答1:


The case of toString() is different, you can do whatever you want with toString() so I will only cover equals() (and hashCode()).

First, the rule: if you want to store an object in a List, Map or a Set then it is a requirement that equals and hashCode are implemented so they obey the standard contract as specified in the documentation.

Now, how to implement equals() and hashCode()? A "natural" idea would be to use the properties mapped as Id as part of the equals():

public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if (id==null) return false;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.id.equals( that.getId() );
    }
    public int hashCode() {
        return id==null ? System.identityHashCode(this) : id.hashCode();
  }
}

Unfortunately, this solution has a major problem: when using generated identifiers, the values are not assigned until an entity becomes persistent so if a transient entity is added to a Set before being saved, its hash code will change while it's in the Set and this breaks the contract of the Set.

The recommended approach is thus to use the attributes that are part of the business key i.e. a combination of attributes that is unique for each instance with the same database identity. For example, for the User class, this could be the username:

public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.username.equals( that.getUsername() );
    }
    public int hashCode() {
        return username.hashCode();
  }
}

The Hibernate Reference Documentation summarizes this as follow:

"Never use the database identifier to implement equality; use a business key, a combination of unique, usually immutable, attributes. The database identifier will change if a transient object is made persistent. If the transient instance (usually together with detached instances) is held in a Set, changing the hashcode breaks the contract of the Set. Attributes for business keys don't have to be as stable as database primary keys, you only have to guarantee stability as long as the objects are in the same Set." - 12.1.3. Considering object identity

"It is recommended that you implement equals() and hashCode() using Business key equality. Business key equality means that the equals() method compares only the properties that form the business key. It is a key that would identify our instance in the real world (a natural candidate key)" - 4.3. Implementing equals() and hashCode()

So, back to the initial question:

  • Use a business key if possible. @Transient attributes are very likely not part of such a key.
  • If not possible, use identifier properties but make sure to get the values assigned before to add an entity to a List, Map, Set.

See also

  • Equals and HashCode
  • Don't Let Hibernate Steal Your Identity
  • equals and hashcode in Hibernate
  • Understanding equals() and hashCode() (p. 396) in Java Persistence with Hibernate



回答2:


The two typical usages of @Transient and transient that I'm aware of, are to use them either for stuff that can't be serialized/persisted (e.g. a remote resource handle) or computed properties which can be reconstructed from others.

For computed data, it makes no sense to use them in the equality relationship (equals/hashCode), because it would be redundant. The value is computed out of other value which are already used in the equality. It can however still makes sense to print them in toString (e.g. a base price and a ratio are used to compute the actual price).

For not serializable/persitable data, it depends. I can imagine a handle to a resource that is not serializable, but you can still compare the resource name that the handle represent. Same for toString, maybe printing the handle resource name is useful.

This was my 2 cent, but if you explain your particular usage of @Transient, someone can maybe give a better advice.




回答3:


Exception maybe comes from letting it be transient and at the same time you provide writeObject() and readObject() where you process it.



来源:https://stackoverflow.com/questions/2951454/should-transient-property-be-used-in-equals-hashcode-tostring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!