Archive for the ‘hibernate’ Category

hibernate and component querying

Wednesday, October 18th, 2006

Well here is some interestingness with querying for components using the 3.2.0cr5 version of hibernate. (FYI using accessor methods is the way the code was actually written. Direct references to properties in this example used just for clarity.)

Components in hibernate are objects that are represented in the database as a subset of columns with in their container. For example:

In the Java code:

class Foo {
    public Peanuts peanut;
    public int foo1;
    public Bar bar;
}

class Bar {
    public Peanuts peanut;
    public int bar1;
}

class Peanuts{
   public Foo foo;
}

     <class name="Foo">
         <many-to-one name="peanut"/>
         <property name="foo1"/>
         <component name="bar">
             <property name="bar1"/>
         </component>
    </class>

will result in a database table ‘FOO’ that looks like:

PEANUT, FOO1, BAR1

Now the problem I have is that I wanted to retrieve Bars. Using a PostLoadEventListener looking for Foo’s, I wanted to fix up the reference Bar had to Peanuts, like this:


    public class FooPostLoadListener implements PostLoadEventListener {
        private static final long serialVersionUID = 1L;

        public void onPostLoad(PostLoadEvent event) {
            if ( event.getEntity() instanceof Foo) {
                Foo ts = (Foo)event.getEntity();
                ts.bar.peanuts = ts.peanuts;
            }
        }
    }

But of course the entity being loaded is a Bar not a Foo.

Now there is the possible use of the <parent> with in the Bar component. However, that means that a publically visible method is available that should never be used. (<parent> doesn’t have the ability to set the property using ‘field’ access).

The next step was creating a setParent(Foo) and a Foo getParent() on bar:


class Bar {
    public Peanuts peanut;
    public int bar1;
    public void setParent(Foo f) {
       peanuts = f.peanuts;
    }
    public Foo getParent() {
        return peanuts.foo;
    }
}

     <class name="Foo">
         <many-to-one name="peanut"/>
         <property name="foo1"/>
         <component name="bar">
             <parent name="parent"/>
             <property name="bar1"/>
         </component>
    </class>

Now this doesn’t work because the setParent() method is called before Foo has any of its fields instantiated. This actually makes a lot of since otherwise when you are reading in the result set, there is a lot of juggling to get the data ordered in the right way for what the code needs.

Unfortunately, inspite of all this rework at the end of it all, querying for a component returns the component without the parent Foo set. I was hoping that accessing the parent Foo via a call to getParent() would trigger a load of Foo but it doesn’t.

Hibernate nastiness using the one-to-one mapping.

Tuesday, October 3rd, 2006

Hibernate nastiness filed as bug HHH-2128. When reading this please notice that this situation arose from the most minimal definition. Using the ‘defaults’ is what caused this issue!

Steps:

  1. define two classes (Primary, Secondary) that have a one-to-one relationship.
  2. use the native generator to generate a primary key for each.
  3. save a primary with no assigned secondary object.
  4. save another primary with a secondary object.
  5. retrieve the first primary and it will have the secondary object saved in previous step.
  6. retrieve the second primary and it will have no secondary object.

Talk about ouch!

Hibernate’s event system

Sunday, October 1st, 2006

So I just had an interesting realization, Hibernate uses various default listeners in it’s poorly documented event system to do all of its persistence work.

For example, saving an object through a Hibernate Session object quickly goes to the Session.fireSaveOrUpdate, which by default calls DefaultSaveOrUpdateEventListener. DefaultSaveOrUpdateEventListener does all the heavy lifting of checking to see if the object legal to save prior to actually saving the object.

These default listeners also invoke interceptors as briefly described in section 12.1 of the 3.2.0.cr4 manual.

This means that if someone starts adding event listeners of their own, they better make damn sure that they add in Hibernate’s as well. Otherwise Hibernate will very quietly do nothing!

I filed this bug in the hopes that the documentation would get changed:

Please add this (or equivalent) to the bottom of section 12.1. (Interceptors):

===========================================
Interceptors are invoked by Hibernate’s default event listeners. For example, interceptors’ onSave() method is invoked by Hibernate’s DefaultSaveOrUpdateEventListener
===========================================

Please add this (or equivalent) to the bottom of section 12.2 (Event System):

===========================================
When changing the event listeners be sure to keep the various Hibernate default listeners. These listeners actually perform much of Hibernate’s persistence work, including invocation of the interceptors. Without those default listeners, Hibernate will quietly do nothing.
===========================================

Event Default Hibernate EventListener Interceptor method called by default listener
PreLoadEvent DefaultPreLoadEventListener onLoad
PreInsertEvent
(return true to veto the insertion)
DefaultSaveOrUpdateEventListener
(on HSQL EntityIdentityInsertAction - which gets the generated id after the insertion)
?
SaveOrUpdateEvent DefaultSaveOrUpdateEventListener onSave
PostInsertEvent
(called even if the PreInsertEvent vetoed the insertion)
DefaultSaveOrUpdateEventListener ?

hibernate messages … nice and unhelpful

Wednesday, September 13th, 2006

One of the reasons why I recommend Tapestry as a web framework is the helpfulness of its error messages. Hibernate is just the opposite.

Current annoying hibernate error message :”Null index column for collection” when trying to examine a previously saved collection.

This seems to be caused by a mysterious IDX column on the class that is to be placed into the map. This column is null for whatever reason. Inserts would succeed but examining the collection would fail.

It turns out this was caused because I was missing a column attribute in the map-key element.

I had this:

<map-key type="long"/>

not this:

<map-key type="long" column="ID"/>