Reputation: 977
Today I spent some time debugging an issue with hibernate, simplified example would look like:
Map<Cat, Owner> catsMap = new HashMap();
List<Owner> owners = ownerRepo.getOwners();
for (Owner owner : owners) {
// cat is Lazy, according to its nature :)
catsMap.put(owner.getCat(), owner);
}
Cat cat = catRepo.findOne("meow");
Owner meowOwner = catsMap.get(cat);
at this moment meowOwner
is null because it is not found in catsMap
keyset. It took sometime to figure out why because in debug window I see that the Cat
with name 'meow' exists in keyset of catsMap
, moreover, if I write an expression
catsMap.keySet().iterator().next().equals(cat)
it returns true
, hashcodes are the same, same values, though
catsMap.get(cat)
still returns null in the same expressions window. At last I called
catsMap.keySet().iterator().next().getClass()
and finally found out that it is long.path.to.package.Cat_$$_jvstaea_41
, so it is a proxy and equals fails on the step when it checks class equality.
The solution is, of course, obvious, but the question is why do I have
catsMap.keySet().iterator().next().equals(cat)
returning true
? I tried also reversed case
cat.equals(catsMap.keySet().iterator().next())
and this one returns false
, which is breaking the equals()
convention of transitivity.
PS: in all the examples I assume that currently there is only one cat and one owner in DB
Upvotes: 2
Views: 527
Reputation: 6473
Cat cat = catRepo.findOne("meow")
should return the same instance, unless your Map
is outside of the initial transaction. If you want to store an Entity outside of a transaction, make sure to unproxy it before storing it.
public T unproxy(T proxied)
{
T entity = proxied;
if (entity instanceof HibernateProxy) {
Hibernate.initialize(entity);
entity = (T) ((HibernateProxy) entity)
.getHibernateLazyInitializer()
.getImplementation();
}
return entity;
}
You have to make sure you use the right syntax for your equals
and hashCode
overrides. This is an example of implementation:
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof MyEntityClass))
return false;
MyEntityClass other = (MyEntityClass) obj;
return Objects.equals(getId(), other.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
Note: Do not use the fields directly this.id
, prefer the getter to allow for Hibernate's proxy to resolve the entity when necessary. Also, prefer instanceof
to getClass() != obj.getClass()
, the first will handle implementations and extends correctly, not the second.
Upvotes: 1