tags:

views:

7250

answers:

5

I have a property on a domain object that is declared in a many-to-one element. The basic syntax of this property looks like this:

<many-to-one name="propertyName" class="propertyClass" fetch="select" not-found="ignore" lazy="proxy" />

Now, the idea is to have Hibernate NOT eagerly fetch this property. It may be null, so the not-found ignore is set.

But, Hibernate, upon loading the class containing this association, takes it upon itself to load the actual class (not even a proxy) instance when the parent class is loaded. Since some properties are over 1MB in size, they take up a lot of the heap space.

If, however, not-found is set to exception (or defaulted to exception), the parent classes which have this property do load a proxy!

How can I stop hibernate from not loading a proxy, while still allowing this property to be null?

I found lazy=no-proxy, but the documentation talks about some sort of bytecode modification and doesn't go into any details. Can someone help me out?

If it matters, it is the Java version of Hibernate, and it is at least version 3 (I can look up the actual version if it helps, but it is Hibernate 3+ for now).

I didn't specify earlier, but the Java version is 1.4. So, Java annotations aren't supported.

A: 

If you're passing the hibernate object from the model to the view via the controller, don't!

Instead make a "snapshot object" to store the values from the Hibernate object you want to pass to the view and be displayed.

Why? The proxy can still retrieve the values when it is in the controller...but when you pass the proxy/object to the view it no longer can retrieve the values because the transaction has already ended. And this is why I have suggested what I have above.

leeand00
except when you use Open Session In View pattern
miceuz
This has nothing to do with the question asked. This isn't about any particular view technology or controller. Even in JUnit tests this behavior happens.The type of object you suggest is a "Data Transfer Object", by the way. But that has no relevance here.
MetroidFan2002
The reason it has no relevance is that the object either is completely loaded when the parent is loaded (with not-found="ignore"), or is loaded via proxy but can throw an exception (with not-found="exception") if null. I want the ability to have it be null, and load it via proxy.
MetroidFan2002
+2  A: 

If the other end of the association can be null, I believe hibernate must query for the association end in order to determine if it should use a proxy or not (no need for proxy if the other end is null). I can't find the reference to this right now, but I remember reading it somewhere.

In order to provide lazy-loading of fields the documentation refers to bytecode enhancements on fields at buildtime: hibernate chapter 19.1.17. Here is an excerpt:

Hibernate3 supports the lazy fetching of individual properties. This optimization technique is also known as fetch groups. Please note that this is mostly a marketing feature, as in practice, optimizing row reads is much more important than optimization of column reads. However, only loading some properties of a class might be useful in extreme cases, when legacy tables have hundreds of columns and the data model can not be improved.

Lazy property loading requires buildtime bytecode instrumentation! If your persistent classes are not enhanced, Hibernate will silently ignore lazy property settings and fall back to immediate fetching.

Miguel Ping
This looks promising...I'll accept your answer if it works out. Thanks!
MetroidFan2002
No problem, but I think basically you can't have a lazy proxy with single ended associations than can be null (one-to-one or many-to-on).As for the huge heap consumption, I had a similar problem because of clobs, I ended up pulling the clobs to another class and loading them as required.
Miguel Ping
I think what we're going to do is put the field in the class that's huge (it's an XML document that is eventually sent to a web service) into the lazy-loaded field, using the bytecode stuff you pointed out. Unfortunately, the domain model is way out of my hands.
MetroidFan2002
A: 

When using Hibernate annotations, putting @ManyToOne(fetch = FetchType.LAZY) on the association, accomplishes what you want. Have you tried setting fetch="lazy" to see if that works?

Joey Gibson
I'm not even sure fetch="lazy" is a valid property for the XML configuration. According to http://www.hibernate.org/hib_docs/v3/reference/en-US/html/mapping.html (5.1.12), fetch is limited to "join|select". Plus, Java version is only 1.4 anyways, so annotations are out. I'll edit the question.
MetroidFan2002
A: 

@Miguel Ping: I think the page you are referring to is [http://www.hibernate.org/162.html]. As far as I understand, the additional SELECT is needed in the case of the one-to-one side, where the foreign key isn't present. Setting constrained="true" tells Hibernate, that the other side is always present and no additional SELECT is needed.

So for the many-to-one side, where the foreign key resides, it shouldn't be necessary to execute another SELECT since the value of the FK tells if the other end is present or null. At least, this is how I understand it.

So far for the theory. The proxy works for me on the foreign key / many-to-one side. The used mapping for the association is:

<many-to-one name="haendler" column="VERK_HAENDLOID" lazy="proxy" />

But proxying doesn't work for me on the one-to-one side using a mapping like the one described at the given URL (constrained="true"). Hmm, think I'll open a question for this. ;-)

rudolfson
+2  A: 

I found lazy=no-proxy, but the documentation talks about some sort of bytecode modification and doesn't go into any details. Can someone help me out?

I'll assume you're using ANT to build your project.

<property name="src" value="/your/src/directory"/><!-- path of the source files -->
<property name="libs" value="/your/libs/directory"/><!-- path of your libraries -->
<property name="destination" value="/your/build/directory"/><!-- path of your build directory -->

<fileset id="applibs" dir="${libs}">
  <include name="hibernate3.jar" />
  <!-- include any other libraries you'll need here -->
</fileset>

<target name="compile">
  <javac srcdir="${src}" destdir="${destination}" debug="yes">
    <classpath>
      <fileset refid="applibs"/>
    </classpath>
  </javac>
</target>

<target name="instrument" depends="compile">
  <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
    <classpath>
      <fileset refid="applibs"/>
    </classpath>
  </taskdef>

  <instrument verbose="true">
    <fileset dir="${destination}">
      <!-- substitute the package where you keep your domain objs -->
      <include name="/com/mycompany/domainobjects/*.class"/>
    </fileset>
  </instrument>
</target>
Civil Disobedient
Please see: http://stackoverflow.com/questions/1444227/making-a-onetoone-relation-lazy/3590989#3590989
Kdeveloper