One issue with the implementation above is that the hashCode and equals
contract can be broken.
"If two objects are equal according to the equals(Object) method, then
calling the hashCode method on each of the two objects must produce the
same integer result."
The consequence of this violation is demonstrated by the following
scenario. Please note that the occurrence of this scenario may be quite
rare.
There is a User class and Role class. The User class has a many-to-many
relationship with Role.
1. Create a new User in a Hibernate session. Call it user1.
2. In another Hibernate session add a new Role to the user. Call it role2.
3. Finally in a third hibernate session load the user from the database.
Test that the role object created in step 2 is in user.roles using
Set.contains(Object)
The test in the third step should return true but instead returns false.
The reason it returns false is because Set.contains(Object) uses
hashCode() before using equals(). equals() returns true when comparing
the Role created in step 2 to the Role loaded in step 3 but the
hashCodes are different.
Here is some sample code that demonstrates the scenario. The comments
should explain what is going on.
// save a User to the database
Session session1 =
HibernateUtil.getSessionFactory().getCurrentSession();
session1.beginTransaction();
User user1 = new User();
session1.save(user1);
session1.getTransaction().commit();
// in a new session add some Roles to the User
// and check that all roles remain in User.roles
Session session2 =
HibernateUtil.getSessionFactory().getCurrentSession();
session2.beginTransaction();
User user2 = (User) session2.load(User.class, user1.getId());
Role role1 = new Role("role1");
Role role2 = new Role("role2");
user2.getRoles().add(role1);
user2.getRoles().add(role2);
session2.update(user2);
session2.getTransaction().commit();
System.out.println(user2.getRoles());
// the statement above prints out "[role2, role1]" This is correct
// in another new session check that User.roles.contains()
// works for a Role created above
Session session3 =
HibernateUtil.getSessionFactory().getCurrentSession();
session3.beginTransaction();
User user3 = (User) session3.load(User.class, user1.getId());
System.out.println(user3.getRoles().contains(role2));
// the statement above prints out false - it is meant to be true.
// This fails because role2.hashCode() does not equals the hashCode
of the Role
// loaded from the database
for (Role role : user3.getRoles()) {
if (role.equals(role2)) {
System.out.println(role.hashCode() + "!=" + role2.hashCode());
}
}
// The loop above prints out
// "1!=-1901084812"
// the hashCode of role2 does not equal the hashCode
// of the equivalent Role in user3.getRoles()
session3.close();
HibernateUtil.getSessionFactory().close(); |