Reputation: 295
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
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
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
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
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