You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Jim Steinberger <st...@dynedge.com> on 2006/05/03 00:42:59 UTC

Insert/Update pages and Hibernate

  In Tapestry, we use model objects as the data-buffer.  If we're
editing an entity, and something goes wrong, we can set an errorMessage
property, "return this;", and expect the user to see the form still
filled-out with what they'd just typed with an explanation of what went
wrong.

  If you're using Hibernate, however, I don't think this use-case is
guaranteed; mainly because I'm currently dealing with cases where it
isn't.

  What I mean by that:  If something goes wrong in a Hibernate session,
your entities are not guaranteed to be left in a stable state.  i.e.
they're not guaranteed to match their counterparts in the persistence
layer.

  In my case, I'm trying to insert an object with some not-null
properties left as null.  Hibernate sets the primary key of my object
(when it queries my Postgres sequence), but then fails on the actual
entity-insert.  An exception is thrown, but the primary key is left on
my object.  Since my Tapestry page is designed to handle both creating
and updating this entity -- and since it does this by testing whether or
not an object's primary key exists -- the user is told there was a
problem, but as far as Tapestry is concerned, the user is now editing
the entity, not creating it.
  I can hack it by explicitly setting the primary key to be null where I
catch the Exception, but who knows what else Hibernate might be mutating
without going through the source code.

  Am I crazy, or does this Hibernate practice mean that, everywhere we
expect to be able to use an object even if something goes wrong in
Hibernate, we have to clone our entity first or open ourselves up to
potential problems?  Are people doing this?


Jim


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


RE: Insert/Update pages and Hibernate

Posted by James Carman <ja...@carmanconsulting.com>.
As Paul said, you can use the transaction-per-request strategy and set the
rollback-only flag if something goes wrong.  This will ensure that nothing
is written to the database, but the POJOs in the session will still remain
untouched.  If you want to get off the ground quite quickly, you can use
"tapernate."  There's an example application (which does exactly what you're
talking about) here:

http://www.carmanconsulting.com/svn/public/tapernate-example/trunk

Download it via SVN.  Others have started using Tapernate and have been
quite pleased with it.  Hopefully it can be helpful to you.

-----Original Message-----
From: Paul Cantrell [mailto:cantrell@pobox.com] 
Sent: Tuesday, May 02, 2006 7:12 PM
To: Tapestry users
Subject: Re: Insert/Update pages and Hibernate

There are several practices you can use to avoid painting yourself  
into this corner:


(1) Probably the best and simplest: Use a straightforward transaction- 
per-request model, and don't ever hold on to persistent objects  
between requests.* Instead, hold on to the IDs of your persistent  
objects. Think of the persistent objects as a fleeting view of the  
app's persistent state, a view that will vanish at the end of the  
request. Keep enough user input and working values in your page to be  
able to get all the way from a cleanly fetched object to a  
committable one, in one go -- instead of poking and prodding the  
persistent object to its ultimate committable state across several  
requests.

Having a custom Tapestry SqueezeAdaptor for your persistent objects  
makes this quite simple: you page properties can all look like the  
full persistent objects, but Tapestry will only store the ID between  
requests.

It sounds like your problems are cause by having a broken persistent  
object dangling from your page. Solution? Don't attach it to the  
page! Throw it away when the request is done. Let your listeners  
fetch it again clean next time.


(2) On the heels of (1), always roll back on error. You can do this  
in your transaction interceptor or servlet filter; it's also possible  
to do it in validator / listener methods. A mix of both may be  
appropriate.

Sounds like you may already be doing this -- but when combined with  
(1), you know that your DB and your objects are clean after a  
failure, and only the page itself remains messy.


(3) Make your domain objects "fail fast:" enforce constraints up  
front, and when possible, don't let objects get into an invalid state  
to begin with. In your case, that means not storing the PK back to  
the page until the create has succeeded.


(4) To make (3) really work, ensure your domain objects observe  
strict failure atomicity. See _Effective Java_ for an explanation.  
(Order a copy if you don't own it! It's a marvelous book.)


I hope that is helpful.

Cheers,

Paul

* In some situations, holding persistent objects between requests is  
the right approach. However, I believe these situations are rare. The  
pitfalls of this approach are large, and the performance benefits are  
often overstated (especially if you use caching).



On May 2, 2006, at 5:42 PM, Jim Steinberger wrote:

>   In Tapestry, we use model objects as the data-buffer.  If we're
> editing an entity, and something goes wrong, we can set an  
> errorMessage
> property, "return this;", and expect the user to see the form still
> filled-out with what they'd just typed with an explanation of what  
> went
> wrong.
>
>   If you're using Hibernate, however, I don't think this use-case is
> guaranteed; mainly because I'm currently dealing with cases where it
> isn't.
>
>   What I mean by that:  If something goes wrong in a Hibernate  
> session,
> your entities are not guaranteed to be left in a stable state.  i.e.
> they're not guaranteed to match their counterparts in the persistence
> layer.
>
>   In my case, I'm trying to insert an object with some not-null
> properties left as null.  Hibernate sets the primary key of my object
> (when it queries my Postgres sequence), but then fails on the actual
> entity-insert.  An exception is thrown, but the primary key is left on
> my object.  Since my Tapestry page is designed to handle both creating
> and updating this entity -- and since it does this by testing  
> whether or
> not an object's primary key exists -- the user is told there was a
> problem, but as far as Tapestry is concerned, the user is now editing
> the entity, not creating it.
>   I can hack it by explicitly setting the primary key to be null  
> where I
> catch the Exception, but who knows what else Hibernate might be  
> mutating
> without going through the source code.
>
>   Am I crazy, or does this Hibernate practice mean that, everywhere we
> expect to be able to use an object even if something goes wrong in
> Hibernate, we have to clone our entity first or open ourselves up to
> potential problems?  Are people doing this?
>
>
> Jim

_________________________________________________________________
Piano music podcast: http://inthehands.com
Other interesting stuff: http://innig.net



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


Re: Insert/Update pages and Hibernate

Posted by Paul Cantrell <ca...@pobox.com>.
There are several practices you can use to avoid painting yourself  
into this corner:


(1) Probably the best and simplest: Use a straightforward transaction- 
per-request model, and don't ever hold on to persistent objects  
between requests.* Instead, hold on to the IDs of your persistent  
objects. Think of the persistent objects as a fleeting view of the  
app's persistent state, a view that will vanish at the end of the  
request. Keep enough user input and working values in your page to be  
able to get all the way from a cleanly fetched object to a  
committable one, in one go -- instead of poking and prodding the  
persistent object to its ultimate committable state across several  
requests.

Having a custom Tapestry SqueezeAdaptor for your persistent objects  
makes this quite simple: you page properties can all look like the  
full persistent objects, but Tapestry will only store the ID between  
requests.

It sounds like your problems are cause by having a broken persistent  
object dangling from your page. Solution? Don't attach it to the  
page! Throw it away when the request is done. Let your listeners  
fetch it again clean next time.


(2) On the heels of (1), always roll back on error. You can do this  
in your transaction interceptor or servlet filter; it's also possible  
to do it in validator / listener methods. A mix of both may be  
appropriate.

Sounds like you may already be doing this -- but when combined with  
(1), you know that your DB and your objects are clean after a  
failure, and only the page itself remains messy.


(3) Make your domain objects "fail fast:" enforce constraints up  
front, and when possible, don't let objects get into an invalid state  
to begin with. In your case, that means not storing the PK back to  
the page until the create has succeeded.


(4) To make (3) really work, ensure your domain objects observe  
strict failure atomicity. See _Effective Java_ for an explanation.  
(Order a copy if you don't own it! It's a marvelous book.)


I hope that is helpful.

Cheers,

Paul

* In some situations, holding persistent objects between requests is  
the right approach. However, I believe these situations are rare. The  
pitfalls of this approach are large, and the performance benefits are  
often overstated (especially if you use caching).



On May 2, 2006, at 5:42 PM, Jim Steinberger wrote:

>   In Tapestry, we use model objects as the data-buffer.  If we're
> editing an entity, and something goes wrong, we can set an  
> errorMessage
> property, "return this;", and expect the user to see the form still
> filled-out with what they'd just typed with an explanation of what  
> went
> wrong.
>
>   If you're using Hibernate, however, I don't think this use-case is
> guaranteed; mainly because I'm currently dealing with cases where it
> isn't.
>
>   What I mean by that:  If something goes wrong in a Hibernate  
> session,
> your entities are not guaranteed to be left in a stable state.  i.e.
> they're not guaranteed to match their counterparts in the persistence
> layer.
>
>   In my case, I'm trying to insert an object with some not-null
> properties left as null.  Hibernate sets the primary key of my object
> (when it queries my Postgres sequence), but then fails on the actual
> entity-insert.  An exception is thrown, but the primary key is left on
> my object.  Since my Tapestry page is designed to handle both creating
> and updating this entity -- and since it does this by testing  
> whether or
> not an object's primary key exists -- the user is told there was a
> problem, but as far as Tapestry is concerned, the user is now editing
> the entity, not creating it.
>   I can hack it by explicitly setting the primary key to be null  
> where I
> catch the Exception, but who knows what else Hibernate might be  
> mutating
> without going through the source code.
>
>   Am I crazy, or does this Hibernate practice mean that, everywhere we
> expect to be able to use an object even if something goes wrong in
> Hibernate, we have to clone our entity first or open ourselves up to
> potential problems?  Are people doing this?
>
>
> Jim

_________________________________________________________________
Piano music podcast: http://inthehands.com
Other interesting stuff: http://innig.net



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