Reputation: 1201
I'm using hibernate 4.3.10.
When refer to composite primary key, my entity looks like next (added equals() and hashCode() after getting the answer from @Master Slave) :
@Entity
@Table(name="compositepk")
public class Car {
@EmbeddedId
private CarPK carPK;
private String name;
public CarPK getCarPK() {
return carPK;
}
public void setCarPK(CarPK carPK) {
this.carPK = carPK;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Embeddable
public static class CarPK implements Serializable{
private static final long serialVersionUID = -5202331188724915048L;
private int chassisNumber;
private int engineNumber;
public int getChassisNumber() {
return chassisNumber;
}
public void setChassisNumber(int chassisNumber) {
this.chassisNumber = chassisNumber;
}
public int getEngineNumber() {
return engineNumber;
}
public void setEngineNumber(int engineNumber) {
this.engineNumber = engineNumber;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof CarPK) {
CarPK car = (CarPK)obj;
if(this.getChassisNumber().intValue() == car.getChassisNumber().intValue() &&
this.getEngineNumber().intValue() == car.getEngineNumber().intValue()) {
return true;
} else {
return false;
}
}
return false;
}
@Override
public int hashCode() {
return this.chassisNumber.hashCode()+this.engineNumber.hashCode();
}
}
}
hibernate doc says we have to implements equals() and hashCode() in composite primary key.
However, I found there is not any problems without overriding them in CarPK. I can compare cars and add them to Set with right result. For instance, the following is comparison code :
Car.CarPK pk = new Car.CarPK();
pk.setChassisNumber(3);
pk.setEngineNumber(2017);
Car c1 = (Car) session1.get(Car.class, pk);
Car c2 = (Car) session2.get(Car.class, pk);
if(c1.equals(c2)) {
System.out.println("==");
} else {
System.out.println("!=");
}
This code prints "!=" and it prints "==" if chassisNumber and engineNumber are the same.
(After modifying the above code, get the same result in different session, the above code prints "!=", why? doesn't it should print out "==" because I have implemented the equals() and hashCode()?)
So can anyone show me the problems when I don't implements equals() and hashCode() in CarPK?
Thanks in advance!
Upvotes: 1
Views: 568
Reputation: 4091
Took me some time to figure it out, but you also must override equals
and hashCode
in Car
@Override
public boolean equals(Object obj) {
if(obj instanceof Car) {
Car that = (Car) obj;
return this.carPK.equals(that.carPK);
}
return false;
}
@Override
public int hashCode() {
return this.carPK.hashCode();
}
By the way, your hashCode
's implementation in CarPK
is dangerous (and false because it doesn't compile). It's too easy to generate 2 CarPK
with the same hashCode
but not equals
Car.CarPK pk1 = new Car.CarPK();
pk1.setChassisNumber(3);
pk1.setEngineNumber(2017);
Car.CarPK pk2 = new Car.CarPK();
pk2.setChassisNumber(1500);
pk2.setEngineNumber(520);
System.out.println(pk1.hashCode()); //prints 2020
System.out.println(pk2.hashCode()); //prints 2020
//Same hashCode, should be equal, just checking...
System.out.println(pk1.equals(pk2)); //prints false !
I recommand you this implementation where it's more difficult to generate collisions
@Override
public int hashCode() {
return Objects.hash(chassisNumber, engineNumber);
}
Upvotes: 1
Reputation: 28569
Your test works because you are in the same session/persistence context. Make a case where you try to load the entities in two different persistent context (again with the same values) and you'll realize that its a different java object.
That is why you must implement a business key equility, so hashCode
and equals
that will tell hibernate how to reason about the object equality and that should holde regardless of the state of the object (transient, attached, detached). Based on the above, an id property is not a good candidate for being the part of the hash/equals contracts as its value is state dependent
from the docs
Hibernate uses the Hibernate session to manage this uniqueness. When you create an object with new(), and then save it into a session, Hibernate now knows that whenever you query for an object and find that particular object, Hibernate should return you that instance of the object. And Hibernate will do just that. However, once you close the Hibernate session, all bets are off. If you keep holding onto an object that you either created or loaded in a Hibernate session that you have now closed, Hibernate has no way to know about those objects. So if you open another session and query for "the same" object, Hibernate will return you a new instance. Hence, if you keep collections of objects around between sessions, you will start to experience odd behavior (duplicate objects in collections, mainly).
Upvotes: 0