Problem with equals () method in sleep mode

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 not a value populated by the user, and is populated using GenerationType.Identity as a strategy .

I also have another Department class 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 bi-directional ManyToOne relationship between 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 . Therefore, 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 that I create new Employee(); I do not have its ID , since it will be assigned when it is saved. Therefore when i say

 existingDepartment.addEmployee(newEmployee); 

The Department internal HashSet effectively uses the equals() method, which is broken [because it uses a member variable to determine an equality that was not correctly initialized).

This seems like a very simple problem, but how can I solve it? Or am I designing my classes incorrectly? Or whether my equals method should be rewritten to compare other values ​​instead of ID , which, I think, would be absurd.

+6
source share
4 answers

This seems like a very simple problem, but how can I solve it? Or am I designing my classes completely wrong? Or my method is equal rewritten to compare other values ​​instead of IDs, which I suppose to be absurd.

There are two different philosophies in this.

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

Disadvantage: you cannot compare permanent and non-permanent objects

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

Disadvantage: two objects with the same identifier may not be equal.

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

This has been discussed many times, btw:

+6
source

Rewrite your equals method to return 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; } 
+1
source

In sleep mode, you can usually specify to use a value when it has not been stored in db. For example, I used -1 for an identifier that has not yet been saved.

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

 private int ID = -1; 
0
source

You can add a transition field with another mutable identifier. (perhaps you should switch to the "long" id). Something like this for an 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 must be sure that the saved and unsaved Employee are not used.

Another strategy is to first save the employee and then add him to the department

0
source

Source: https://habr.com/ru/post/896131/


All Articles