Home

Advertisement

Dec. 18th, 2006

TabbedPane Bug Fixes

I found and fixed a couple pretty serious bugs in the processDecodes and saveDescendentState methods of UITabbedPane. These bugs resulted in the following behavior:

  • ValueChangeListeners, converters, and validation listeners were applied to the new tab instead of the tab that was just visible, which mean:
    • Events do not fire correctly
    • Request values are not applied correctly
    • Hibernate LazyInitializationExceptions if your tabs are backed by lazy loading entities
  • EditableValueHolder value bindings were evaluated during APPLY REQUEST VALUES, which causes:
    • Hibernate LazyInitializationExceptions if your tabs are backed by lazy loading entities
    • Unpredictable results (when combined with the above behavior)
  • Value binding expressions to be stored as local values, thus preventing evaluation after INVOKE APPLICATION

These have all been resolved with some very minor changes to code. Additionally, I've added some static methods to UITabbedPane, findTabbedPaneAncestor and findTabbedPaneDescendent, that are useful for obtaining a reference to an enclosing or enclosed UITabbedPane component (since component binding is not yet available). I've also added the ability to set the selected tab index programmatically via a new setSelectedTabIndex method on UITabbedPane.

The distribution package has been updated. It can be found here:

sweetfaces.tar.gz

The next update will improve facelets support by adding true property accessors to UITabbedPane and UITab to avoid "attribute XXX not found" warnings, and by removing the "Invalid child component of UITabbedPane" messages (which happen as a result of the UIInstruction components inserted by facelets).

Tags: ,

Nov. 30th, 2006

Hibernate Backref

Under certain circumstances Hibernate will issue the following error:

org.hibernate.PropertyValueException: not-null property references a null or transient value: foo.Bar._barsBackref at org.hibernate.engine.Nullability.checkNullability(Nullability.java:72) at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:265) at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167) at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:114) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186) at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175) at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70) at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:524) at org.hibernate.impl.SessionImpl.save(SessionImpl.java:514) at org.hibernate.impl.SessionImpl.save(SessionImpl.java:510) ...

This happens when:

  • There two or more entities have a collection of foo.Bars; and
  • The collection mappings are inverse="false"; and
  • The collection associations all map to the same table; and
  • The association of at least one of those entities is not-null="true"

For example, suppose the following mappings:

Foo.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="foo" schema="foo"> <class name="Foo" table="foo"> <id name="id" column="foo_id" unsaved-value="null"> <generator class="native"> <param name="sequence">sq_foos</param> < </id> <version name="version"/> <set name="bars" table="foo_bar" cascade="all-delete-orphan" lazy="true"> <key column="foo_id" not-null="true"/> <many-to-many class="foo.Bar" column="bar_id"/> </class> </hibernate-mapping>
Oof.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="oof" schema="foo"> <class name="Oof" table="oof"> <id name="id" column="oof_id" unsaved-value="null"> <generator class="native"> <param name="sequence">sq_oofs</param> < </id> <version name="version"/> <set name="bars" table="oof_bar" cascade="all-delete-orphan" lazy="true"> <key column="oof_id" not-null="true"/> <many-to-many class="foo.Bar" column="bar_id"/> </class> </hibernate-mapping>

Here we have two classes, Foo and Oof, that hava a many-to-many association to Bar. (A common scenario would be to have Account and Order entities that both have many-to-many associations with a Contact entity.)

What happens is that somehow in Hibernate these two mappings get coalesced into a single "bars" persister. (I suspect this is because they are the same class mapped to the same table.) This persister tracks all of the properties in Bar plus backrefs (back references) for each of the entities that contain "bars". In the above example there would be two additional values, _fooBackref and _barBackref.

During a flush operation, Hibernate checks nullability of all of these values, including the backrefs. It checks all of the backrefs, regardless of what the owning entity is. If we're saving a Foo with a bunch of Bars in it, then the _oofBackref would necessarily be null and would fail the check, resulting in the stack trace above.

There are a couple of ways to work around this issue:

  • Remove not-null="true" from all collection associations; or
  • Make the collection mappings bidirectional with inverse="true"

In the case of many-to-many associations the first option isn't a problem, since many-to-many inserts must happen after the inserts on both sides of the associations, meaning the key values will never be null anyway. (I've only ever encountered this problem in many-to-many scenarios.) The second solution may work better for one-to-many associations.

Tags:

Oct. 17th, 2006

Java 5 Generics, Polymorphism, and Hibernate

I ran into an interesting problem today while working with Hibernate. After loading an object graph and committing the session (with no modifications to the object graph) I noticed that Hibernate was issuing updates and deletes similar to the following:

update my_table set fk_column=null where id_column=? delete from my_table where id_column=?

I also began to notice sporadic occurrences of the following error:

org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: my.package.Class.collectionProperty

The properties referenced in the HibernateExceptions were the same properties associated with the updates and deleted. The root cause seems to have something to do with the fact that these properties were:

  • Inherited from a base class
  • Prototyped using generics

The base class was something similar to this:

public class EntityWithReferences extends Entity { protected Set _references = new LinkedHashSet(); public Set getReferences() { return _references; } public void setReferences(Set references) { _references = references; } public void addReference(T reference) { if(reference != null) _references.add(reference); } public Set getReferences(String system) { Set sysRefs = new LinkedHashSet(); for(T reference: _references) { if(reference.getSystem().equals(system)) { sysRefs.add(reference); } } return sysRefs; } // Other common reference behavior }

And my subclasses were something like this:

public class Account extends EntityWithReferences { // Account properties and behavior }

Naturally each subclass has its own corresponding subclass of Reference, hence the generics. What I found though is that somehow Hibernate thinks that these inherited collections are not associating with the "owning entity" (the subclasses of EntityWithReferences). By getting rid of the generic base class and moving the reference behavior into the subclasses, all of the symptoms disappeared. The resulting subclasses now look something like this:

public class Account extends Entity { public Set _references = new LinkedHashSet(); public Set getReferences() { return _references; } public void setReferences(Set references) { _references = references; } public void addReference(AccountReference reference) { if(reference != null) _references.add(reference); } public Set getReferences(String system) { Set sysRefs = new LinkedHashSet(); for(AccountReference reference: _references) { if(reference.getSystem().equals(system)) { sysRefs.add(reference); } } return sysRefs; } // Other reference behavior // Account properties and behavior }

This isn't ideal, as now I have to carry all of this biolerplate code around for reference handling, but it does solve my immediate problems. Here's hoping that this will save someone else a lot of time.

Tags:

Sep. 19th, 2005

Hibernate Transactions and Units of Work

I won't go into depth about Hibernate or the design considerations that it brings, as there is ample documentation on the Hibernate web site. One particularly useful document is Session and Transaction Scope, which elucidates on the concepts of application (user) transactions and units of work. Suffice to say, that having experimented amply with both design patterns, session-per-request and session-per-application-transaction, I strongly prefer the former, as it provides a lot more insight and control over when data is persisted. I find it is much easier and there are far fewer surprises than with long application transactions.

I would like to note that in most cases there is a very fine line between the session-per-request pattern and what the author of the above document calls a session-per-operation anti-pattern. It is common to load an object into memory in one request and save it in the next request. Two operations in two requests averages out to be one session per operation. Because sessions are lightweight and because they cache data I do not consider session-per-operation to be anti-pattern from any standpoint other than performance, which can be remedied using a second-level cache. Of course, whenever possible it is always best to group operations in a single unit of work to enhance performance, provided it is convenient and you are not violating any transaction semantics by doing so.

The most common method for implementing the session-per-request pattern is to use the ThreadLocal Session pattern, which works well in a servlet container using filters, or in a Struts action class. It does not, however, isolate all of the concerns that are typical of every transaction. For example, although the commitTransaction method of HibernateUtil will attempt a rollback if the transaction commit fails, it will not attempt a rollback if there is an error prior to the commit, such as when a business exception that occurs mid-transaction.

The ideal solution would be to isolate those aspects (transaction error handling and recovery) by wrapping the persistence operations with a boilerplate try/catch/finally block. This could be done by generating proxies for servlets, JSF backing beans, or Struts actions, but there is no support for that at this time. The Spring framework AOP facilities may be able to accomodate this approach, but I am not familiar with Spring.

The solution I came up with was to create an abstract class called UnitOfWork with an abstract method named doWork( ) and a final method named execute( ) that wraps doWork( ) with the appropriate error handling logic. In the request handler methods, anonymous inner classes extending UnitOfWork can be created inline that contain the persistence operation. Here is the code:

UnitOfWork.java
public abstract class UnitOfWork
{
    private Throwable _transactionException = null;
    private Throwable _commitException = null;
    private Throwable _rollbackException = null;
    private Throwable _closeException = null;
    
    private boolean _committed = false;
    private boolean _rolledBack = false;
    private boolean _closed = false;
    
    protected Session _session = null;
    protected Transaction _transaction = null;
    
    public Throwable getTransactionException( )
    {
        return _transactionException;
    }    
    
    public Throwable getCommitException( )
    {
        return _commitException;
    }
    
    public Throwable getRollbackException( )
    {
        return _rollbackException;
    }
    
    public Throwable getCloseException( )
    {
        return _closeException;
    }
    
    public boolean isCommitted( )
    {
        return _committed;
    }
    
    public boolean isRolledBack( )
    {
        return _rolledBack;
    }
    
    public boolean isClosed( )
    {
        return _closed;
    }
    
    public abstract Object doWork( Object[] arguments )
    throws Throwable;
    
    private final void init( )
    throws Throwable
    {
        _transactionException = null;
        _commitException = null;
        _rollbackException = null;
        _closeException = null;
    
        _committed = false;
        _rolledBack = false;
        _closed = false;
    }
    
    public final Object execute( )
    throws UnitOfWorkException
    {
        return execute( null );
    }
    
    public final Object execute( Object[] arguments )
    throws UnitOfWorkException
    {
        Object returnValue = null;
        try
        {
            _session = HibernateUtil.openSession( );
            _transaction = _session.beginTransaction( );
            init( );
            returnValue = doWork( arguments );
            try
            {
                commit( );
                _committed = true;
            }
            catch( Throwable commitException )
            {
                _commitException = commitException;
            }    
        }
        catch( Throwable transactionException )
        {
            _transactionException = transactionException;
        }
            
        if( !_committed )
        {
            try
            {
                rollback( );
                _rolledBack = true;
            }
            catch( Throwable rollbackException )
            {
                _rollbackException = rollbackException;
            }
        }

        try
        {
            close( );
            _closed = true;
        }
        catch( Throwable closeException )
        {
            _closeException = closeException;
        }
        
        _session = null;
        _transaction = null;
        
        if( !_committed )
        {
            throwException( );
        }
        else if( !_closed )
        {
            logException( );
        }
        return returnValue;
    }
    
    public void commit( )
    throws Throwable
    {
        if( _transaction != null )
        {
            _transaction.commit( );
        }
    }
    
    public void rollback( )
    throws Throwable
    {
        if( _transaction != null )
        {
            _transaction.rollback( );
        }
    }
    
    public void close( )
    throws Throwable
    {
        if( _session != null )
        {
            _session.close( );
        }
    }
    
    private void throwException( )
    throws UnitOfWorkException
    {
        UnitOfWorkException uowe = buildException( );
        if( uowe != null )
        {
            throw uowe;
        }
    }
    
    private UnitOfWorkException buildException( )
    {
        UnitOfWorkException uowe = null;
        if( _transactionException != null )
        {
            uowe = new UnitOfWorkException( this, _transactionException );
        }
        
        if( (_commitException != null) && (uowe == null) )
        {
            uowe = new UnitOfWorkException( this, _commitException );
        }
        
        if( (_rollbackException != null) && (uowe == null) )
        {
            uowe = new UnitOfWorkException( this, _rollbackException );
        }
        
        if( (_closeException != null) && (uowe == null) )
        {
            uowe = new UnitOfWorkException( this, _closeException );
        }
        
        return uowe;
    }
    
    private void logException( )
    {
        UnitOfWorkException uowe = buildException( );
        // Commons logging, J2SE logging, Log4j, or System.out.println here
    }
}


UnitOfWorkException.java
public class UnitOfWorkException
extends RuntimeException
{
    private UnitOfWork _unitOfWork = null;
    
    public void setUnitOfWork( UnitOfWork unitOfWork )
    {
        _unitOfWork = unitOfWork;
    }
    public UnitOfWork getUnitOfWork( )
    {
        return _unitOfWork;
    }
    
    public UnitOfWorkException( )
    {
    }
    
    public UnitOfWorkException( String message )
    {
        super( message );
    }
    
    public UnitOfWorkException( Throwable cause )
    {
        super( cause );
    }
    
    public UnitOfWorkException( String message, Throwable cause )
    {
        super( message, cause );
    }
    
    public UnitOfWorkException( UnitOfWork unitOfWork )
    {
        _unitOfWork = unitOfWork;
    }
    
    public UnitOfWorkException( UnitOfWork unitOfWork, String message )
    {
        super( message );
        _unitOfWork = unitOfWork;
    }
    
    public UnitOfWorkException( UnitOfWork unitOfWork, Throwable cause )
    {
        super( cause );
        _unitOfWork = unitOfWork;
    }
    
    public UnitOfWorkException( UnitOfWork unitOfWork, String message, Throwable cause )
    {
        super( message, cause );
        _unitOfWork = unitOfWork;
    }
    
    public void printStackTrace( PrintStream out )
    {
        printStackTrace( new PrintWriter( out ) );
    }
    
    public void printStackTrace( PrintWriter out )
    {
        super.printStackTrace( out );
        if( _unitOfWork != null )
        {
            if( _unitOfWork.getCommitException( ) != null )
            {
                out.print( "Upon commit: " );
                _unitOfWork.getCommitException( ).printStackTrace( out );
            }

            if( _unitOfWork.getRollbackException( ) != null )
            {
                out.print( "Upon rollback: " );
                _unitOfWork.getRollbackException( ).printStackTrace( out );
            }

            if( _unitOfWork.getCloseException( ) != null )
            {
                out.print( "Upon close: " );
                _unitOfWork.getCloseException( ).printStackTrace( out );
            }
        }
    }
}


And here is an example of how it would be used as an anonymous inner class:

Example 1
...
    final String itemName = "Example";
    Item item = (Item)new UnitOfWork( )
    {
        public Object doWork( Object[] arguments )
        throws Throwable
        {
            String name = (String)arguments[0];
            // Create a new ItemDao using the session variable prepared
            // by the UnitOfWork execute( ) method and placed in the
            // _session instance variable of the UnitOfWork class
            return new ItemDao( _session ).getByName( name );
        }
    }.execute( new Object[] { itemName } );
    System.out.println( "My item: " + item );
    ...


In this example, all of the error handling and recovery has been replaced with an anonymous inner class declaration. Arguments and return values can be passed to and from the doWork( ) method through the execute( ) method. The only possible exception that can be thrown from execute( ) is a UnitOfWorkException, which has placeholders for exceptions that can occur at all phases of the execute( ) method. When the method returns, the programmer can be assured that all cleanup has been accomplished and the best possible effort was made to gracefully recover from any errors that might have occurred. The init( ), commit( ), rollback( ), and close( ) methods can be overridden to perform additional initialization and cleanup operations as needed (file descriptors, Oracle temporary LOBs, etc.)

Note that outer class instance variables are accessible within inner classes, so we could have changed the code to be like this:

Example 2
public class MyClass
{
    private Item _item = null;
    private final String _itemName = "Example";

    ....

    public void myMethod( )
    {
        ...
        new UnitOfWork( )
        {
            public Object doWork( Object[] arguments )
            throws Throwable
            {
                _item = new ItemDao( _session ).getByName( _itemName );
                return null;
            }
        }.execute( );
        System.out.println( "My item: " + item );
        ...
    }
    ...
}


Some notes about this design pattern:


  • The transaction and error handling logic operates only on the session created by the execute( ) method

    • The code in the doWork( ) method is free to use any available session, or create its own, but doing so circumvents the error handling logic and defeats the purpose of the UnitOfWork class

    • All work should be done using the session object stored in the _session instance variable, either directly or through Data Access Objects (DAOs) that use the session specified by the caller



  • There is no need for a filter or other interceptor

  • The session and transactions are opened and closed in the execute( ) method. All initialization and cleanup is transparent to the programmer.

    • Lazy associations must be fetched within a UnitOfWork doWork( ) method

    • All objects are detached outside of the UnitOfWork doWork( ) method

    • Changes made to detached objects will not persist unless the objects are explicitly updated or merged



  • A UnitOfWork anonymous class be used to implement either the session-per-operation pattern (or anti-pattern if you like) or the session-per-request pattern depending on the content of the doWork( ) method.

    • The contents of the doWork( ) method can consist of one operation or a hundred, so long as they all belong to one logical database transaction.



  • Works well with servlets and Struts

    • Servlet class can extend UnitOfWork and implement javax.servlet.Servlet. The service( ) method of the servlet can perform some initialization and simply call execute( ) when ready (no anonymous inner class required).

    • Struts action classes require anonymous inner classes (or outside helper classes), as Action classes must extend Action, and there may be more than one unit of work if DispatchAction is used.

    • JSF beans should use anonymous inner classes (or outside helper classes) as there will likely be more than one action method per backing bean



Advertisement

Customize