user801138
user801138

Reputation: 295

Problem with equals() method in Hibernate

I am developing an application in Hibernate where I have model classes like these:

public class Employee
{
    private int ID;
    private String name;
    private Department department;
    //other properties
    //constructors, getters and setters
}

Note that the ID is not a value populated by the user and is populated using GenerationType.Identity as the strategy.

Also I have another class Department as follows:

public class Department
{
    private int ID;
    private String name;

    private Set<Employee> employees; //this is actually a HashSet

    //other implementations
}

There is a ManyToOne bi-directional relationship between an Employee and a Department.

So to add a new Employee to an existing Department, I do the following

Department existingDepartment = ...;
Employee newEmployee = ...;

existingDepartment.addEmployee(newEmployee);
employee.setDepartent(existinDepartment);

session.save(newEmployee);

Now conceptually two Employee objects are the same if they have the same ID. So my equals() method in the Employee class looks like this:

public boolean equals(Object o)
{
    if(!(o instanceOf Employee))
    {
        return false;
    }

    Employee other = (Employee)o;

    if(this.ID == o.etID())
    {
        return true;
    }

    return false;
}

Now the problem is when I create a new Employee(); I do not have its ID, since it will be assigned when it will be persisted. So when I say

existingDepartment.addEmployee(newEmployee);

the internal HashSet of the Department object is effectively using an equals() method which is broken [since it uses a member variable to determine equality that as not been initialized properly].

This seems like a very basic problem but, how do I solve it? Or am I designing my classes totally wrong? Or should my equals method be re-written to compare other values instead of ID, which I guess would be absurd.

Upvotes: 6

Views: 2908

Answers (4)

stefan bachert
stefan bachert

Reputation: 9606

You may add an transient field with an different non-persistent id. (maybe you should upgrade to "long" id). Something like this for Example

public class Employee {
  private static int lastTID = 0;
  private int ID = -1;
  private transient int tID;
 ..
 public Employee () {
    synchronized (getClass()) {
      tId = -- lastTID;
    }
 }
 public boolean equals(Object o) {
 ..
   Employee other = (Employee)o;
 ..
   if (ID != -1) {
    return ID == other.ID;
   } else {
    return other.ID == -1 && tID == other.tID;
   }
 }

In any case you have to assure that there are not saved and unsaved Employee in use.

Another strategy is to save Employee first and then to add it to Department

Upvotes: 0

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 299008

This seems like a very basic problem but, how do I solve it? Or am I designing my classes totally wrong? Or should my equals method be re-written to compare other values instead of ID, which I guess would be absurd.

There are two different philosophies concerning this.

a) equals() / hashCode() based on DB id

Drawback: you can't compare persistent and non-persistent objects

b) equals() / hashCode() based on contents

Drawback: two objects with the same id may turn out to be non-equal.

I prefer the second approach, it makes more sense from a Java point of view (although admittedly not from a DB point of view). The only thing I'd want to make sure is that you never mix the approaches.

This has been discussed many times before, btw:

Upvotes: 7

chzbrgla
chzbrgla

Reputation: 5188

Rewrite your equals method, so that it returns false, when o is null:

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final Employee other = (Employee) obj;
    if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
        return false;
    }
    return true;
}

Upvotes: 1

Amir Raminfar
Amir Raminfar

Reputation: 34169

In hibernate usually you can tell it to use a value when it hasn't been saved to db. For example, I have used -1 for an ID which hasn't been stored yet.

You should initialize your ids like this to make sure you get a consistent behavior.

private int ID = -1;

Upvotes: 0

Related Questions