在hibernate复合主键中没有覆盖equals和hashCode的查找

there is find without overidding equals and hashCode in hibernate composite primary key

我正在使用休眠 4.3.10。

当引用复合主键时,我的实体看起来像下一个(从@Master Slave 得到答案后添加了 equals() 和 hashCode()) :

@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 文档说我们必须在复合主键中实现 equals() 和 hashCode()。

但是我发现在CarPK中不覆盖是没有问题的。我可以比较汽车并将它们添加到 Set 中并得到正确的结果。例如下面是比较代码:

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("!=");
}

此代码打印“!=”,如果 chassisNumber 和 engineNumber 相同,则打印“==”。

(修改上面的代码后,在不同的session中得到相同的结果,上面的代码打印“!=”,为什么?不应该打印出“==”吗,因为我有实现了 equals() 和 hashCode()?)

所以当我没有在 CarPK 中实现 equals() 和 hashCode() 时,有人能告诉我问题吗?

提前致谢!

您的测试有效,因为您处于相同的 session/persistence 上下文中。假设您尝试在两个不同的持久上下文中加载实体(同样具有相同的值),您将意识到它是一个不同的 java 对象。

这就是为什么你必须实现业务键平等,所以 hashCodeequals 将告诉休眠如何推理对象平等,并且无论对象的状态如何都应该保持(短暂的,附加的,分离的)。基于以上所述,id 属性 不适合作为 hash/equals 合约的一部分,因为它的值取决于状态

来自 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).

我花了一些时间才弄明白,但你还必须覆盖 Car

中的 equalshashCode
@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();
}

顺便说一句,您的 hashCodeCarPK 中的实现是危险的(并且是错误的,因为它无法编译)。用相同的hashCode生成2个CarPK太容易了,但不是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 !

我向您推荐这个更难产生碰撞的实现

@Override
public int hashCode() {
    return Objects.hash(chassisNumber, engineNumber);
}