SQL Zone is brought to you in partnership with:

I'm a software developer. I'm passionate, I like what I'm doing and I try to do it better every day. I like open technologies because that's where I'm coming from. Currently working as a freelancer on J2EE applications. Particularly interested in Scala, Liftweb and Functional Programming. Andrew has posted 14 posts at DZone. You can read more from them at their website. View Full User Profile

Hibernate Gotchas!

08.25.2011
| 9503 views |
  • submit to reddit

I've been using Hibernate for some time now and when I don't work on a hibernate project for a while I find myself doing the same mistakes I did the previous times

So here is a sort of watch list for my self hopefully will be useful to someone else as well.

Implement hashCode and equals You should always implement these methods in general but for your entities you should pay a bit more attention.The first thing you think when I say equals is probably to use the Id to distinguish between instance. Well, that's gonna cause you a lot of troubles.
You need to keep in mind that you are working with db entities and not normal POJOs.

When Hibernate is fetching objects is your using collections and hence equals and hashCode to know if an object you are looking for is in the session. For new objects id will be null or 0.
That means when trying to save two objects of the same class the second is going to overwrite the first one.
Also when hibernate saves a new instance it will set the id, thus making it a different object while it is exactly the same.
You need to use some business keys. Unique codes are great but if you can't think of anything just use a meaningful field and some timestamp (like creation date) to make it unique.

This is a good reference if you want to understand a bit further what's happening.

Careful with One-to-One and Many-to-One relations This is something you really need to know.
When mapping a relation as One-to-One or Many-to-One on the "One" side of the relation you can't have lazy loading unless you specify the field as not nullable.

Why is that?
Essentially on the many side of the relation hibernate can use collection proxies and lazily load instances when required.
On the "One" side there is no collection interface but instead a reference to one of your model classes.
Hibernate can proxy that one as well but only if it is sure the reference will never be null!
So remember if you want to have lazy loading use the not null on the one side together with the lazy annotation (or xml equivalent).
If your relation can be null but you still really want to make it lazy then you have some options:
  • Create a value to represent that. For example if you have a relation like Person ->Partner  just use a specific instance of Partner that means "no partner".
  • Use build time instrumentation. Check this
  • Fake the one side using a List and getting the field with get(0)
Read more on the hibernate documentation
Enable the statement logging
This is the only way to verify Hibernate is really doing what you expect him to do. Luckily enough there are different logging parameters that you can use to find out what is happening both at the HQL or if you want at the SQL level. You'll be surprised how many times hibernate is running queries and you did not except it. Try to this from the very beginning and help the team understand the importance of having the best and least possible queries or you'll surely have performance issue when running the application on some real data. To enable logging just set this property in the session configuration file
hibernate.show_sql=true
If you want to see it nicely formatted add
hibernate.format_sql=true
Watch what goes in the toString method. This one is again related to what Hibernate fetches for you without you really being aware. Lots of times when you see queries but can't figure out why some lazy list is being loaded then check the toString method.
It might be the culprit!


What are your hibernate gotchas?

From http://www.devinprogress.info/2011/08/hibernate-gotchas.html

Published at DZone with permission of its author, Andrew Salvadore.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Fab Mars replied on Fri, 2011/08/26 - 7:06am

I my personal projects, I keep equals and hashCode based on the Id. But you have to be carful. My golden recipe:

public interface Identifiable<T extends Comparable<? super T>> extends Serializable {
  public abstract T getId();
}

Then in a first abstract implementation:

public boolean equals(Object obj) {
  if(obj instanceof Identifiable<?>) {
    return EntityUtils.equalsId(this, (Identifiable<?>) obj);
  }
  else {
    return false;
  }
}

public String toString() {
  return new StringBuilder()
  .append(getClass().getName())
  .append("[").append(getId()).append("]@")
  .append(Integer.toHexString(hashCode())
  .toString();
}

With:

public static boolean equalsId(Identifiable<?> o1, Identifiable<?> o2) {
  if (o1 == o2) { // includes null == null
    return true;
  }
  else if (o1 != null && o2 != null) {
    return (o1.getClass().isInstance(o2) || o2.getClass().isInstance(o1)) && safeEquals(o1.getId(), o2.getId());
  }
  else {
    return false;
  }
}

private static boolean safeEquals(Object o1, Object o2) {
  return (o1 == o2 // includes null == null
      || (o1 != null && o1.equals(o2)) || (o2 != null && o2.equals(o1)));
}

And finally I have a second abstract class for all those entities that use a <T extends Number & Comparable<T>> as their Id, and it contains:

public int hashCode() {
  if(getId() != null) {
    return getId().intValue();
  }
  else { //return something negative not to have session collisions
    int hash = super.hashCode(); //Object#hashCode
    return (hash > 0) ? -hash : hash;
  }
}

 That never failed me.

Yohan Liyanage replied on Sat, 2011/08/27 - 3:42am in response to: Fab Mars

@Fab,

In the suggested implementation, what would happen if two entities of the same type had null IDs, such as in a situation where auto-generated IDs are used ? I think then the equals check will return true indicating that those two entities are the same.

For example, consider a situation where you have two unsaved Customer objects (say one is Fred and one is Bob), based on the suggested implementation. If the ID field is autogenarated,  the ID will be null before saving.

The equals check will pass because the objects are from same class, and have same ID (null).

If you add this to a collection class which uses equals, then it will be treated as the same, although those two are two different customers.

Kevin Jordan replied on Sat, 2011/08/27 - 10:55am

I generally have my equals method check the ids first if they are not null since if they're the same, they should be the same object in the database. If both are null then I check all the remaining fields to be the same. Not sure how often equals would get used on an unpersisted object by hibernate though and I generally try not to float unpersisted objects too long in my code if I can help it.

Aaron Digulla replied on Mon, 2011/08/29 - 11:24am

Interesting thought; why don't the equals() implementations check for "both ids != null && id1 == id2" first? That would be a short cut before comparing all the business values. Doesn't help with hashCode(), though :-/ Alternatively, you could throw an error if an id is null but I'm not sure how maintainable such code would be.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.