Allen:
Your suggested pattern is workable, but you'll need to make begin() and
commit() conditional and smart about transaction demarcation; it is a
common pattern to conditionally begin a transaction only if there isn't
one on the current thread, and you'll find that in many infrastructures
(see my comments on Section 2 in the design proposal).
What happens if you don't have that logic? You'll find that the
operations that you choose to make transactional can't be easily
composed, which is a very awkward property to have. If you ever need to
compose two operations, you would need to write a third method that
includes all of the operations of the originals, and not just calling
the two original methods, but copying their innards. I actually went
through a period of trying to code apps this way in the 90's before
infrastructures like EJB started to normalize the conditional
demarcation patterns in Java. Lesson learned.
The patterns for transaction demarcation in the dispatch layers involve
three parts: (1) demarcation for requests on a per-request basis in a
Filter or some other centralized dispatch point (e.g. in Axis or Struts
apps in extensions of these servlets wrapping the entry points) (2) for
scheduled tasks, wrapping the run() method in a base class (3) providing
a mechanism to get an independent transaction for individual units of
work at smaller granularities (REQUIRES_NEW style semantics), which I
expect would be used very rarely in Roller. Patterns like this work
well for two-tier web apps like Roller where the persistence container
and the webapp container are the same. They don't work (or require much
stronger transaction infrastructure) if you separate the webapp
container from the persistence container which does not apply in
Roller's case.
Like your proposal, the dispatch-level patterns remove begin() and
commit() from app code, and we should end up with only a few places that
call them.
--a.
Allen Gilliland wrote:
On Thu, 2006-03-09 at 14:21, David M Johnson wrote:
On Mar 9, 2006, at 5:02 PM, Allen Gilliland wrote:
i feel like we are still a little disconnected here. my approach
will not alter anything about our ability to use the open session
in view pattern. the only difference i see is the difference
between these 2 code blocks ...
old way:
begin();
website.store();
commit();
new way:
WeblogManager wmgr = roller.getWeblogManager();
wmgr.saveWebsite(website);
But what if saving a website is just one step in a multi-part
transaction? OK, scratch that. If you start by doing the analysis and
designing one of the new Manager interfaces, I think we'll have a
much better understanding of this proposed refactoring.
i'll just give the short answer now. usually things that are multipart
operations are still part of a single logical operation. in fact, we already
do this in some places. take a look at the UserManager.createWebsite() method.
that is a logical method which wraps a series of persistence operations. so
the difference would be ...
old way (in struts action or servlet):
begin();
// setup website
website.store();
// setup template
template.store();
// setup category
category.store();
// etc etc
commit();
new way (logical method in manager class):
UserManager umgr = roller.getUserManager();
umgr.createWebsite(newWebsite, possible, other, data);
when i use the new way i only care wether or not the operation succeeded and i
don't have to think about the persistence implications because they are hidden
from me. now if it turns out that we have way too many transactions that would
need their own logical method in a manager class then maybe this approach won't
work, but i believe that in most request/response cycles the operations are
pretty isolated and simple. in most of them you are only
creating/updating/deleting a single object at a time.
-- Allen
- Dave