Question

I am encountering a weird behavior when trying to see if two sets are equal. I have overridden the equals and hashcode:

public class Metric {


    private String id;

    private Sensor sensor;

    private String metricName;

    private String metricDescription;

    //getters, setters, toString()...

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((metricDescription == null) ? 0 : metricDescription.hashCode());
        result = prime * result + ((metricName == null) ? 0 : metricName.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Metric other = (Metric) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (metricDescription == null) {
            if (other.metricDescription != null)
                return false;
        } else if (!metricDescription.equals(other.metricDescription))
            return false;
        if (metricName == null) {
            if (other.metricName != null)
                return false;
        } else if (!metricName.equals(other.metricName))
            return false;
        return true;
    }

}

I have intentionally left the Sensor information out of the hashCode and equals, but in both so it should not make a difference.

Now, consider the following code:

SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.getCurrentSession();
    Transaction tx = null;

    try {
        tx = session.beginTransaction();
        session.save(datasource);
        session.save(sensorType);
        session.save(sensor);
        session.save(metric1);
        session.save(metric2);
        session.getTransaction().commit();
    } catch (HibernateException e) {
        if (tx != null)
            tx.rollback();
        throw e;
    } finally {
        if (session.isOpen())
            session.close();
    }

    try {
        session = sessionFactory.getCurrentSession();
        tx = session.beginTransaction();
        sameSensor = (Sensor) session.get(Sensor.class, new String(sensor.getId()));
    } catch (HibernateException e) {
        if (tx != null)
            tx.rollback();
        throw e;
    } finally {
        if (session.isOpen())
            session.close();
    }

    // simply retrieve the metrics
    Set<Metric> sensorMetrics = sensor.getMetrics();
    Set<Metric> sameSensorMetrics = sameSensor.getMetrics();
    System.out.println("SENSOR METRIC");
    for(Metric m : sensorMetrics) {
        System.out.println(m.getMetricName() + ":" + m.hashCode());
        System.out.println(sameSensorMetrics.contains(m));
    }
    System.out.println("SAME SENSOR METRIC");
    for(Metric m : sameSensorMetrics) {
        System.out.println(m.getMetricName() + ":" + m.hashCode());
        System.out.println(sensorMetrics.contains(m));

    }

The two sets should be identical (and all the hascodes are), but I am getting the following result:

SENSOR METRIC
metric2name_addSensor_1393695505000:-1437647480
true
metric1name_addSensor_1393695505000:2040143911
true

SAME SENSOR METRIC
metric1name_addSensor_1393695505000:2040143911
false
metric2name_addSensor_1393695505000:-1437647480
false

Even though the metrics are the same (I have checked the equals on all the pairs, the results are correct) the metrics are contained in one set but not the other... I really can't explain this and would appreciate any help.

I have given code snippets that I believe are the important ones, if any more information is needed I will be more than happy to provide. Thanks.

EDIT:

1) The code where Sensor is initialized as dfb requested:

/*
 * Create all the sensor-related information to insert
 */

DataSource datasource = new DataSource();
datasource.setDatasourceName(
        createUniqueString("datasource","addSensor"));
datasource.setDatasourceDescription(
        createUniqueString("datasource","addSensor","description"));

SensorType sensorType = new SensorType();
sensorType.setSensorTypeName(
        createUniqueString("sensortype","addSensor"));
sensorType.setSensorTypeDescription(
        createUniqueString("sensortype","addSensor","description")); 

Sensor sensor = new Sensor();
sensor.setDatasource(datasource);
sensor.setSensorType(sensorType);
sensor.setSensorName(createUniqueString("sensorname","addSensor"));
sensor.setSensorDescription(createUniqueString("sensordesc","addSensor","description"));

Metric metric1 = new Metric();
metric1.setMetricDescription(
        createUniqueString("metric1name","addSensor","description"));
metric1.setMetricName(
        createUniqueString("metric1name","addSensor"));
metric1.setSensor(sensor);

Metric metric2 = new Metric();
metric2.setMetricDescription(
        createUniqueString("metric2name","addSensor","description"));
metric2.setMetricName(
        createUniqueString("metric2name","addSensor"));
metric2.setSensor(sensor);

sensor.addMetric(metric1);
sensor.addMetric(metric2);

And the Sensor constructor:

Sensor() {
    this.metrics = new HashSet<Metric>();
}

2) The weird behavior seems to be the Sensor instance that was saved, not the one that was loaded:

Set<Metric> sensorMetrics2 = sensor.getMetrics();
Set<Metric> sensorMetrics = sensor.getMetrics();
System.out.println(sensorMetrics2.equals(sensorMetrics));
System.out.println(sensorMetrics.equals(sensorMetrics));

Set<Metric> sameSensorMetrics2 = sameSensor.getMetrics();
Set<Metric> sameSensorMetrics = sameSensor.getMetrics();
System.out.println(sameSensorMetrics2.equals(sameSensorMetrics));
System.out.println(sameSensorMetrics.equals(sameSensorMetrics2));

The results:

false
false

true
true

No correct solution

OTHER TIPS

Following construct is problematic with Hibernate, because hibernate tends to create proxies for entities because of need for lazy loading:

if (getClass() != obj.getClass())
  return false;

Proxy is subclass of Metrics and thats why code above does not work, but code likely does:

if (!(obj instanceof Metrics)) {
  return false;
}

More instructions about overriding equals and hashcode for entities can be found from Hibernate documentation. Also instanceof can bring surprises, as is told in this blog post.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top