You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Richard Perfect <ri...@rperfect.net> on 2003/11/18 19:48:19 UTC

Re[2]: Hibernate session management in tapestry pages

Hi Glen,

You can also get more architecturally pure and separate database code
into one layer that doesn't know anything about Tapestry - that way
it's easier to migrate at a later time. I'm quite new to Tapestry but
have been using Hibernate for well over a year now and what we've done
is write a "Service" approach that intercepts calls to services in
order to handle transaction management - a bit like EJBs but without
all of the weight of EJBs.

Our Tapestry code looks like;

public void saveAction(IRequestCycle requestCycle) {
    IPersonService personService = (IPersonService)ServiceFactory.getInstance().find(IPersonService.class);
    personService.save(getPerson());

    // Go back to the PersonSearch page
    PersonSearchPage personSearchPage = (PersonSearchPage)requestCycle.getPage("PersonSearch");
    personSearchPage.search();
    requestCycle.activate(personSearchPage);
}

Where the ServiceFactory returns a interceptor ServiceProxy (a Dynamic
Proxy) that deals with the transaction management.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = null;
    Throwable thrown = null;
    try {
        //System.out.println("before method " + method.getName());
        targetService.begin();
        result = method.invoke(targetService, args);

        // only reached this if no exception is thrown
        targetService.commit();
    }
    catch (InvocationTargetException e) {
        targetService.rollback();
        thrown = e.getTargetException();
    }
    catch (Exception e) {
        targetService.rollback();
        thrown = e;
    }
    finally {
        //System.out.println("after method " + method.getName());
    }

    if( thrown != null ) {
        throw thrown;
    }
    else {
        return result;
    }
}

The design consequences are;
 - There's only one ServiceProxy
 - Transaction management is isolated within one place
 - Concrete Service classes must implement an interface (a bit like
   the Remote interface from EJBs)
 - Service level code has no import dependancy on Tapestry and can be
   used independantly of Tapestry
 - Services can call other services but there's only one transaction
   (it's not easy to see here but it does happen)
 - The ServiceFactory can potentially return different kinds of
   services for different reasons. For example we started out with
   Castor but then migrated to Hibernate, for about five development
   releases there were a mixture of some services using Castor and
   some using Hibernate - but the application code doesn't care.

Not all of the code has been shown here, if anyone would like to see
more send me an email.

 - Richard Perfect.
   


Tuesday, November 18, 2003, 2:26:16 PM, you wrote:

GS> Thanks Paul,

GS> You've given me a lot to think about.  Comments inline.

GS> At 11:42 AM 18/11/2003, you wrote:
>>Personally, I think that since most pages will not perform transactions 
>>that it's best not to hard wire a transaction context to a page.  Since 
>>transactions will most likely occur in IActionListeners (triggered from 
>>form submissions),

GS> Well I was thinking that you might not necessarily start the transaction 
GS> until you call something like super.beginTrans().  This way you don't start 
GS> anything unless you need it.

>>you might want to implement a custom action listener -
>>something like this perhaps:
>>
>>public class TransactableActionListener implements IActionListener
>>{
>>    public abstract void transact(Session session, IComponent component, 
>> IRequestCycle cycle) throws HibernateException;
>>
>>    public final void trigger(IComponent component, IRequestCycle cycle)
>>    {
>>        // Wouldn't it be better to store your SessionFactory in the 
>> global object
>>        // rather than a reference in each Visit object (i.e. servlet 
>> context instead of HttpSession)?
>>        Visit visit = ( (Visit) cycle.getPage().getVisit() );
>>        Session session = visit.openSession();
>>        Transaction transaction = null;
>>        try
>>        {
>>            transaction = session.beginTransaction();
>>            transact(session, component, cycle);
>>            transaction.commit();
>>        }
>>        catch ( HibernateException e )
>>        {
>>            HibernateUtil.rollback( transaction );
>>            throw e;
>>        }
>>        catch ( RuntimeException e )
>>        {
>>            HibernateUtil.rollback( transaction );
>>            throw e;
>>        }
>>        finally
>>        {
>>            HibernateUtil.tryClose( session );
>>        }
>>    }
>>}
>>
>>In your page, rather than supplying an action listener the traditional way 
>>(which uses Tapestry class enhancement), you would provide an accessor to 
>>your own action listener impl:
>>
>>public IActionListener getFormSubmitListener()
>>{
>>    return new TransactableActionListener()
>>    {
>>        public void transact(Session session, IComponent component, 
>> IRequestCycle requestCycle) throws HibernateException
>>        {
>>             // Put business logic here
>>        }
>>    };
>>}
>>
>>What do you think?

GS> Nice.  It doesn't completely solve my needs at the moment but comes 
GS> close.  I also have an issue with passing sessions to component models 
GS> (tacos tree) which the ThreadLocal pattern might handle for me nicely.

>>As an alternative solution, you may also want to check out the ThreadLocal 
>>Session pattern if you haven't already:
>>http://www.hibernate.org/42.html

GS> I hadn't.  It looks like it might be what I need too.

>>Hope this helps,
>>
>>Paul Ferraro

GS> Big help, thanks.


---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-user-help@jakarta.apache.org