You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Glen Stampoultzis <gs...@iinet.net.au> on 2003/11/18 00:54:27 UTC

Hibernate session management in tapestry pages

Hi,

I'm currently using the following pattern when obtaining hibernate sessions 
in my pages.

         Visit visit = ( (Visit) getVisit() );
         Session session = visit.openSession();
         Transaction transaction = null;
         try
         {
             transaction = session.beginTransaction();

             transaction.commit();
         }
         catch ( HibernateException e )
         {
             HibernateUtil.rollback( transaction );
             throw e;
         }
         catch ( RuntimeException e )
         {
             HibernateUtil.rollback( transaction );
             throw e;
         }
         finally
         {
             HibernateUtil.tryClose( session );
         }


The problem here is that I'm repeating the same logic all over the 
place.  I'm wondering if it would be possible to subclass BasePage so that 
I can setup a session at the start of the page cycle, tear it down when the 
page cycle is finished and commit or rollback depending whether things went 
well or badly.  Where would be the best place to hook these functions?

Regards,


Glen Stampoultzis
gstamp@iinet.net.au
http://members.iinet.net.au/~gstamp/glen/


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


Re[2]: Hibernate session management in tapestry pages

Posted by Richard Perfect <ri...@rperfect.net>.
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


Re: Hibernate session management in tapestry pages

Posted by Glen Stampoultzis <gs...@iinet.net.au>.
Thanks Paul,

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

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),

Well I was thinking that you might not necessarily start the transaction 
until you call something like super.beginTrans().  This way you don't start 
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?

Nice.  It doesn't completely solve my needs at the moment but comes 
close.  I also have an issue with passing sessions to component models 
(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

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

>Hope this helps,
>
>Paul Ferraro

Big help, thanks.

Re: Hibernate session management in tapestry pages

Posted by Paul Ferraro <pm...@columbia.edu>.
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), 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?

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

Hope this helps,

Paul Ferraro


Glen Stampoultzis wrote:

>
> Hi,
>
> I'm currently using the following pattern when obtaining hibernate 
> sessions in my pages.
>
>         Visit visit = ( (Visit) getVisit() );
>         Session session = visit.openSession();
>         Transaction transaction = null;
>         try
>         {
>             transaction = session.beginTransaction();
>
>             transaction.commit();
>         }
>         catch ( HibernateException e )
>         {
>             HibernateUtil.rollback( transaction );
>             throw e;
>         }
>         catch ( RuntimeException e )
>         {
>             HibernateUtil.rollback( transaction );
>             throw e;
>         }
>         finally
>         {
>             HibernateUtil.tryClose( session );
>         }
>
>
> The problem here is that I'm repeating the same logic all over the 
> place.  I'm wondering if it would be possible to subclass BasePage so 
> that I can setup a session at the start of the page cycle, tear it 
> down when the page cycle is finished and commit or rollback depending 
> whether things went well or badly.  Where would be the best place to 
> hook these functions?
>
> Regards,
>
>
> Glen Stampoultzis
> gstamp@iinet.net.au
> http://members.iinet.net.au/~gstamp/glen/
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
>



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