开发者

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 开发者_JS百科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.


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:

  • Should I write equals() methods in JPA entities?
  • What is the best practice when implementing equals() for entities with generated ids
  • JPA : not overriding equals() and hashCode() in the entities?
    (etc.)


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;
}


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;


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜