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