Well I inject methods but I use a special working thread or at least you
can do. There is this PerThreadManager service (lance, barry and thiago
knows better) and the Job API of Tapestry. There are plenty of ways to get
this done. Barry is writing pages for every batch job and those pages just
return OK (or whatever) to indicate if the job was done or they contain
error messages including the cause (Am I right on this Barry?). He triggers
the pages from within Hudson and hudson does the collecting errors, notify
him by emails about any errors etc. Its a very creative utilization of a
Continous Integration Server if you ask me.

I for myself prefer to use a transactional meaning over CommitAfter -
therefore controlling the transaction myself. You can use
session.getTransaction().isActive to know if you are inside an transaction
and with session.getTransaction().commit/rollback yourself. It makes more
sense to do so in my opinion.

And yes of cause at the end of your batch when you are finally finished
with it you might want to ensure the transaction is committed (test for
active and commit). If you want to use the hibernate session manager of
tapestry you should set the session to auto-clear (clear on
commit/rollback) the way I mentioned. Therefore you do not worry about
clear. It is the way sessions should behave according the JPA specification.

So yes I try to avoid commit after. I introduced my own Transactional
behaviour and pushed logic to the session to ensure I never be able to call
save or update or whatever outside of an transaction and also allow using
read only transactions (which is best in the clustering environment we use
(multi master - slave configurations)).

I replaced the Hibernate???Advisor and the Hibernate ??? Session Source in
my project to be able to reset the test-database after each test to avoid
restarting tapestry (and an embedded jetty) all the time) during the test
(tapestry needs to have 400ms each start, and about 4secs on first start,
if you use jetty you are likely to get even more startup time). Since I use
a in memory database during testing and developing it is quite fast
reducing the startup time < 1ms to <50ms per test. I always start with the
same test scenario but having multiple users in multiple state to test all
white box test cases.

So in the end you are completely right. Just go ahead. But remember you do
not need flush before commit and rollback (in case of an rollback a flush
is also wasted afford since the flushed information are scraped on rollback
anyways) and you need to clean after the commit/rollback not before since
you will only clean some information that are already gone since you
flushed those information to the database. What you try to do is reduce the
number of managed entities and hibernate needs to have those entities being
committed.

Well the last part is not exactly true. You can detach those entities by
hand using evict. But this is troublesome. So if you need your batch job to
be done inside one big transaction you can still speed everything up by
detaching (evict) all those entities you are sure (!) you dont use again
during the very same session (well they get auto attached if you use them
but this might lead to two entity object representing the same database row
(entity) which will cause Hibernate to fail with exception leaving the
session in an unpredictable state you should consider to be broken.

Well that was quite some amount of text. Hopefully you get some additional
use out of it.


Cheers,

Martin (Kersten)



2013/10/25 George Christman <gchrist...@cardaddy.com>

> Thanks Martin, if I understood you correctly, you are still Injecting the
> Session. Will this cause any issues where I'm still using @CommitAfter?
> Also in the batch job, do you typically put an additional commit outside
> the loop to pickup up  the remaining values when they don't reach the
> count, say % 250 == 0 mark or do you just put an additional condition in
> there to commit when you hit the last record?
>
> so something like this,
>
> if(count % 250 == 0 || count == size)
>
>
> On Fri, Oct 25, 2013 at 10:03 AM, Martin Kersten <
> martin.kersten...@gmail.com> wrote:
>
> > well you are decorating the session not the session source of cause... .
> >
> >
> > 2013/10/25 Martin Kersten <martin.kersten...@gmail.com>
> >
> > > >> Perhaps you want a @MaybeCommitAfter ;)
> > >
> > > What about simply having @WriteTransaction (und @ReadTransaction, since
> > > lacking support for read transactions is the biggest performance issue
> > when
> > > dealing with databases). If you are inside a method annotated with
> > > @WriteTransaction it is ignored. If nesting write within a read
> > transaction
> > > fails (there is something wrong with your service methods / thinking).
> > And
> > > also wrapping the session source (decorate) and checking on any
> > > save+/update/delete  + query if a transaction was set makes it crystal
> > > clear if you messed up the transactional thingy. Also using
> @CommitAfter
> > > inside a @XxxxTransaction fails, since you can not commit inside it
> > without
> > > breaking the meaning.
> > >
> > > This would make it possible to have a good implementation which is self
> > > explaining, would only cost two hours to build and test (at least I
> > havent
> > > spent an hour in total I think). And it wont break the backward
> > > compatibility with @CommitAfter.
> > >
> > >
> > > 2013/10/25 Martin Kersten <martin.kersten...@gmail.com>
> > >
> > >> And George, read this one:
> > >>
> >
> http://stackoverflow.com/questions/10143880/hibernate-queries-much-slower-with-flushmode-auto-until-clear-is-called
> > .
> > >> It explains why you experience the slow downs and what is it causing
> it.
> > >>
> > >>
> > >> 2013/10/25 Martin Kersten <martin.kersten...@gmail.com>
> > >>
> > >>> You might also want to use such kind of a utility that allows you to
> > run
> > >>> in transaction:
> > >>>
> > >>> public interface InTransaction<T> {
> > >>>       public T run(Session session);
> > >>> }
> > >>>
> > >>> public static <T> T DatabaseUtil.process(InTransaction<T>
> > >>> transactionalRunnable) {
> > >>>       try {
> > >>>            Session session = currentSession(); //or newSession()
> > >>>            if(!session.getTransaction().isActive())
> > >>>                  session.beginTransaction();
> > >>>            T result = transactionalRunnable.run(session);
> > >>>            if(!session.getTransaction.isActive()) {
> > >>>                session.getTransaction().commit();
> > >>>                session.clear();
> > >>>            }
> > >>>       }
> > >>>       catch(RuntimeException e) {
> > >>>            if(session.getTransaction().isActive()) {
> > >>>                 session.getTransaction().rollback();
> > >>>                 session.clear(),
> > >>>            }
> > >>>       }
> > >>>       finally {
> > >>>            //session.close(); //if you created a new one
> > >>>       }
> > >>>  }
> > >>>
> > >>>
> > >>> This way you can control the whole transactional process while only
> > >>> doing:
> > >>>
> > >>> List<Product> product = DatabaseUtil.process(new
> > >>> InTransaction<List<Product>>() {
> > >>>       List<Product> run(Session session) {
> > >>>            //getUser etc are all static methods fetching from a
> > >>> ThreadLocal RequestContext object so you dont need
> > >>>            //to inject / pass along parts of your model in every
> > >>> component/page
> > >>>            UserInfo user = getUser();
> > >>>            userStats.countUserRequest(user); //changes the database;
> > >>>            return queryProductsForUser(user, session); //returning a
> > list
> > >>>       }
> > >>> }
> > >>>
> > >>> This example is just made up to demonstrate that you can return a
> > >>> List<Product> or Integer or if you have nothing to return
> > >>> a Void instance.
> > >>>
> > >>> The DatabaseUtil class I use for everything that happends outside of
> a
> > >>> request therefore when I have to control the sessions
> > >>> being opened, closed and manage the transaction. This way you even
> not
> > >>> run into any difficult with nesting (you can not
> > >>> leave the transaction (since it is an enclosed try catch block),
> > >>>
> > >>> I have set my session to automatically clear the persistence context
> on
> > >>> commit.
> > >>> You can do this by ((SessionImpl)session).setAutoClear(true).
> > >>>
> > >>> And of cause checking if a transaction is active can be extracted to
> a
> > >>> private method to value the DRY principle.
> > >>>
> > >>>
> > >>>
> > >>>
> > >>> 2013/10/25 Martin Kersten <martin.kersten...@gmail.com>
> > >>>
> > >>>> >              session.flush();
> > >>>> >              session.clear();
> > >>>> >              hibernateSessionManager.commit();
> > >>>>
> > >>>> This is wrong.
> > >>>> First on commit you will do the flush automatically (flush means all
> > >>>> changes are written to the database (performing outstanding updates,
> > >>>> inserts, deletes))
> > >>>> Clear clears the persistence context of all entities not taking part
> > on
> > >>>> any outstanding flush event (as far as I remember) therefore
> > Hibernate does
> > >>>> a deep
> > >>>> inspection of the active entities and removes all entities that were
> > >>>> not encountered during that process.
> > >>>> Commit commits the entities.
> > >>>>
> > >>>> So the correct usage is:
> > >>>> session.getTransaction().commit();
> > >>>> session.clear();
> > >>>> session.beginTransaction();
> > >>>>
> > >>>> (without the clear its what HibernateSessionManager is doing with
> the
> > >>>> session bound to the current thread).
> > >>>>
> > >>>>
> > >>>>
> > >>>>
> > >>>> 2013/10/25 Martin Kersten <martin.kersten...@gmail.com>
> > >>>>
> > >>>>> Use:
> > >>>>>
> > >>>>> @Inject
> > >>>>> Session session; //current session bound to the current thread
> > >>>>>
> > >>>>>
> > >>>>> or
> > >>>>>
> > >>>>> @Inject
> > >>>>> HibernateSessionSource source; + source.create() for a really new
> > >>>>> session (CommitAfter would not work with newly created one);
> > >>>>>
> > >>>>> Using the Manager does give you only the session associated with
> the
> > >>>>> current thread as would @Inject Session session; would do.
> > >>>>>
> > >>>>>
> > >>>>>
> > >>>>>
> > >>>>> 2013/10/25 George Christman <gchrist...@cardaddy.com>
> > >>>>>
> > >>>>>> So I guess I'm still a little confused as to what is the best to
> do
> > >>>>>> it.
> > >>>>>> @CommitAfter seems to work fine for individual transactions but
> does
> > >>>>>> not
> > >>>>>> work well with batch jobs do to it holding on to the object in
> > memory.
> > >>>>>> Anyhow, I could not figure out how to get Martins
> > >>>>>> session.getTransaction()
> > >>>>>> to work, however I did end up getting the following code to work.
> > >>>>>> Could
> > >>>>>> someone tell me if I'm doing this correctly? Also should I be
> > closing
> > >>>>>> and
> > >>>>>> rolling back the transaction?
> > >>>>>>
> > >>>>>>      //Mock scenario
> > >>>>>>
> > >>>>>>     @Inject
> > >>>>>>     private HibernateSessionManager hibernateSessionManager;
> > >>>>>>
> > >>>>>>     public void onActionFromTest() {
> > >>>>>>         Session session = hibernateSessionManager.getSession();
> > >>>>>>
> > >>>>>>         for (int i = 0; i < 100000; i++) {
> > >>>>>>             employee = new Employee("George " + i);
> > >>>>>>
> > >>>>>>             session.save(employee);
> > >>>>>>
> > >>>>>>             if (i % 250 == 0) {
> > >>>>>>                 session.flush();
> > >>>>>>                 session.clear();
> > >>>>>>                 hibernateSessionManager.commit();
> > >>>>>>             }
> > >>>>>>         }
> > >>>>>>
> > >>>>>>         session.flush();
> > >>>>>>         session.clear();
> > >>>>>>         hibernateSessionManager.commit();
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>>>>> On Fri, Oct 25, 2013 at 9:07 AM, Thiago H. de Paula Figueiredo <
> > >>>>>> thiag...@gmail.com> wrote:
> > >>>>>>
> > >>>>>> > On Fri, Oct 25, 2013 at 10:53 AM, Barry Books <trs...@gmail.com
> >
> > >>>>>> wrote:
> > >>>>>> >
> > >>>>>> > While it's true you can run into problems by nesting
> @CommitAfter
> > >>>>>> the same
> > >>>>>> > > can be said about nesting any commits. The Tapestry database
> > >>>>>> model is
> > >>>>>> > > simple. There is one connection per request and when you call
> > >>>>>> commit it
> > >>>>>> > > does a commit.
> > >>>>>> > >
> > >>>>>> >
> > >>>>>> > <pedantic>
> > >>>>>> > Tapestry itself doesn't have any database model. It's a web
> > >>>>>> framework and
> > >>>>>> > nothing else. You can use it with any database, including none.
> > >>>>>> > Tapestry-Hibernate is a package that provides *simple* support
> for
> > >>>>>> > Hibernate and should be used in *simple* scenarios. If you need
> > >>>>>> something
> > >>>>>> > that's not simple, like any transaction handling not supported
> by
> > >>>>>> > @CommitAfter, use Tapestry and some transaction handler (EJB,
> > >>>>>> Spring-TX,
> > >>>>>> > etc) but not Tapestry-Hibernate.
> > >>>>>> > </pedantic>
> > >>>>>> >
> > >>>>>> >
> > >>>>>> >
> > >>>>>> > >
> > >>>>>> > >
> > >>>>>> > > On Fri, Oct 25, 2013 at 7:31 AM, Lance Java <
> > >>>>>> lance.j...@googlemail.com
> > >>>>>> > > >wrote:
> > >>>>>> > >
> > >>>>>> > > > I'm assuming a fork is broken too because it's no good for
> > >>>>>> eating soup?
> > >>>>>> > > > Sounds like you need a spoon, it's easy to write your own
> > >>>>>> annotation...
> > >>>>>> > > > Perhaps you want a @MaybeCommitAfter ;)
> > >>>>>> > > >
> > >>>>>> > >
> > >>>>>> >
> > >>>>>> >
> > >>>>>> >
> > >>>>>> > --
> > >>>>>> > Thiago
> > >>>>>> >
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>>>>> --
> > >>>>>> George Christman
> > >>>>>> www.CarDaddy.com
> > >>>>>> P.O. Box 735
> > >>>>>> Johnstown, New York
> > >>>>>>
> > >>>>>
> > >>>>>
> > >>>>
> > >>>
> > >>
> > >
> >
>
>
>
> --
> George Christman
> www.CarDaddy.com
> P.O. Box 735
> Johnstown, New York
>

Reply via email to