Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a final method prevent Hibernate from creating a proxy for such an entity?

Tags:

hibernate

Hibernate uses proxies to enable lazy loading of collections and even single-ended associations. According to Hibernate's (3.6.5) reference documentaion (Section 21.1.3, Single-ended association proxies), such a proxy can't be constructed by Hibernate if it contains "any final methods".
My question is, does this restriction apply to getters/setters of persistent fields only or really to any method in an entity class? So, does a method like this one:

public final String toString() {
   return this.getClass().getSimpleName() + id;
}

really prevent the creation of a (CGLIB or Javassist) proxy for this entity? Does it matter if field-based or property access is used? Since CGLIB was replaced by Javassist, does this provide any more features in this direction?

I like to use inheritance in my entity hierarchy and hence the requirement to define some final methods, for example, in the base class to prevent subclasses from overriding those methods.

thanks in advance!

like image 852
Robin Avatar asked Jul 07 '11 09:07

Robin


2 Answers

By the help of the Hibernate mailing list (thanks Emmanuel Bernardt!) I'm able to answer my own question, the summary is:
Final methods do not prevent Hibernate from creating a proxy in general but unless those methods don't use any state of the entity this is highly inadvisable.

Some background information: Hibernate doesn't use bytecode enhancement neither with cglib nor with Javassist so, in order for a proxy to initialize its target entity lazily it has to intercept any method which may use the state of that target entity. Now it's perfectly ok to have a final method like this

public final doSomething(String a, Integer b ) {
  // do complicated stuff using only a and b (no instance members accessed!)
}

but as soon as this method uses any persistent field directly or through another instance method this would bypass the proxy and thus lead to unexpected behaviour.

As a sidenote this is the same reason why you should not access fields of other instances directly, for example in an entities equals method:

// XXX bad code!
public boolean equals(Object o) {
  if (this == o) return true;
  if (!(o instanceof Profile)) return false;
  Profile profile = (Profile) o;
  // XXX this bypasses a possible proxy, use profile.getName() instead!
  return (name == null ? profile.name == null : name.equals(profile.name));
}
like image 70
Robin Avatar answered Nov 15 '22 09:11

Robin


I'm pretty sure that, as the reference says, it applies to any method. Indeed, a proxy, in my understanding, is nothing more than a subclass of the entity which has no state except for the ID of the entity initially, and which delegates every method call to an actual instance of the entity class once initialized. It thus has to override all of the methods in order to

  • initialize itself if necessary
  • delegate the call to the actual instance method
like image 32
JB Nizet Avatar answered Nov 15 '22 11:11

JB Nizet