There are a number of rules that JPA entities must obey:
- equals and hashCode must only be based on the persistent fields that are @Id annotated.
- annotations must be applied to either fields or getters, no mix & match (although future versions of the spec may provide for such)
- In general, getters and setters should be simple methods (i.e. no complex processing)
Hello. Stephen.
ReplyDeleteCan I ask somethin'?
I understand @Id annotation criteria for equals() method implementation.
Do I have to include the parent mapping entity?
Say.
@Entity
class Child {
@ManyToOne()
@JoinColumn(name=Parent.TABLE_NAME)
public Parent getParent() { ... }
@Id
public getId() { ...}
@Override
public boolean equals(Object obj) {
// Do I have to check the parent property here?
}
}
Ha!
ReplyDeleteIf only it was that easy...
The answer is that it depends.
I think you have to check the primary key of one object is equal to the primary key of the other...
But then there's the fun of how to make equals consistent across persisting an object... since that is when the primary key gets assigned... and hashCode is more problematic
I would start by asking Google about "Hibernate and equals()" such a search should lead you to the 36 page debate about how to implement equals...
Hopefully JavaEE6 will give better guidelines.
The basic problem is that equals and hashCode are used to find objects inside containers (like HashSet's)
If the hashCode or equals of an object changes over time, then you can actually loose objects inside a HashSet or a HashMap.
If you don't override hashCode, then you could already have the same database object in question stored in your HashSet as a different instance...
If you override hashCode, then you have to solve the problem of how do I get the hashCode of an object before it is persisted (and hence before it has the id on which I can get the hashCode)
One solution is to assign a UUID to every object when you create it... can be heavy
Another solution is to assign the UUID the first time hashCode is called...
In either of those strategies, the UUID has to be the @Id field and you are using application generated Id's as opposed to JPA generated...
All in all it's a bit of a mess.
Have fun!
Hello. Stephen.
ReplyDeleteThank you for you reply.
Hmm.. Interesting problem..ha?
I though I know how and why is the equals()/hashCode() pair works and needed.
You know, Joshua Block..
Implementing the logical equality over memory address equality has been so easy in normal java runtime.
But, when it come to ORM, there comes the problem what I mentioned.
Well, my situation seems to be a little bit easy one.
Both @Id annotated primary keys(one for each) have fully business meaning.
(I used to use UUID as primary key, but I want it as simple as possible at this time)
Here comes again.
@Entity class Parent{
@Id public String getId() { ...}
@OneToMany(mappedBy="parent") public Child getChild() { ...}
@Overrided public boolean equals() { /* compare id and id only */ }
@Overrided public int hasCode() { /* return id == null ? 0 : id.hashCode() */ }
.. has more instance variables...
}
@Entity class Child {
@Id public String getId() { ... }
@ManyToOne()
@JoinColumn(name=Parent.TABLE_NAME)
public Parent getParent() { ... }
@Override public boolean equals(Object obj) { /* compare parent and this.id */ }
}
@Override public int hashCode() { /* hash parent and this */ }
}
What would you say, if
1. Those id values in both classes (which are immutable Strings) have to be served in construction time, never null and never changes (not setter)
2. I just forgot the 2nd, I think I had two conditions in mind for your answer. sorry.
Is there any common reason for separating equality strategy between persistent and (pre/post)non-persistent time?
Anyway, I got an negative answer from some spec guy, but..
Have you ever heard about supporting large object streaming in JPA?
I shocked when I realize that
@Lob and @Basic(fetch=LAZY) combination doesn't solve the memory problem for a big db object (Blob/Clob)
and the spec guy' answer is that 'no plan for this on JPA 2.0'
OpenEJB's @Persistent annotation seems work for this.
Are you an TopLink geek? Is there any workaround for this?
Best regards.
Jin Kwon
I think you'll have to look elsewhere.... I've been dragged away from JPA stuff
ReplyDelete