Pergunta

I have a Java EE Application with Spring 3.1.1 and Hibernate 4.1. Now I wanted to speed up things and saw that the bottleneck is the opening + closing of multiple transactions in one request.

Now I removed all @Transactional annotations and created my own OpenSessionInViewFilter, which opens and closes one transaction.

package utils.spring;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate4.SessionFactoryUtils;
import org.springframework.orm.hibernate4.SessionHolder;
import org.springframework.orm.hibernate4.support.OpenSessionInViewFilter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class CustomHibernateSessionViewFilter extends OpenSessionInViewFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        SessionFactory sessionFactory = lookupSessionFactory(request);
        boolean participate = false;

        if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
            // Do not modify the Session: just set the participate flag.
            participate = true;
        } else {
            logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
            Session session = openSession(sessionFactory);
            TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
                    //BEGIN TRANSACTION
            session.beginTransaction();
        }

        try {
            filterChain.doFilter(request, response);
        }

        finally {
            if (!participate) {
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
                            // COMMIT
                sessionHolder.getSession().getTransaction().commit();
                logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
                SessionFactoryUtils.closeSession(sessionHolder.getSession());
            }
        }
    }
}

Is that a good idea? It seems to work and speeded up things.

Here is my log for the transactions:

http-bio-8080-exec-3 01/03/2013 11:25:20,947 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | doGetTransaction | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
http-bio-8080-exec-3 01/03/2013 11:25:20,948 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | getTransaction | Creating new transaction with name [by2.server.service.UserService.loadUserByUsername]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
http-bio-8080-exec-3 01/03/2013 11:25:20,948 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | doBegin | Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
http-bio-8080-exec-3 01/03/2013 11:25:21,172 | DEBUG | org.springframework.orm.hibernate4.HibernateTransactionManager | doBegin | Exposing Hibernate transaction as JDBC transaction [org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler@1e7b64f4[valid=true]]
http-bio-8080-exec-3 01/03/2013 11:25:21,188 | DEBUG | org.hibernate.SQL | logStatement | select userentity_.userID as userID5_ from users userentity_ where userentity_.username=?

connection pool

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-c3p0</artifactId>
        <version>4.1.1.Final</version>
     </dependency>

<property name="hibernateProperties">
        <value>
            hibernate.hbm2ddl.auto=update
            hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
            hibernate.bytecode.use_reflection_optimizer=false
            hibernate.max_fetch_depth=0
            hibernate.c3p0.min_size=5
            hibernate.c3p0.max_size=20
            hibernate.c3p0.timeout=300
            hibernate.c3p0.max_statements=50
            hibernate.c3p0.idle_test_period=3000
            </value>
    </property>

but the open session in view filter seems to close the sessions

finally {
        if (!participate) {
            SessionHolder sessionHolder =
                    (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
            logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
            SessionFactoryUtils.closeSession(sessionHolder.getSession());
        }
    }

But even when I remove the filter, Hibernate doesn't seem to use the pool.

Foi útil?

Solução

I would say no.

For example, you'll save some product or whatever in the database, and show a success page or redirect, thinking everything has been saved. But the transaction won't be committed yet and could still rollback after you have displayed the success message.

And with Hibernate, the probability of this happening is even bigger, because nothing will be written to the database until flush time, which will happen just before the commit.

Moreover the transaction will live longer than necessary, preventing other transactions to procees in case it has put a lock on rows or tables in the database, resulting in poorer performance and scalability.

What's wrong with the default Spring OpenSessionInViewFilter, which lets the session open, but still uses service-level, short transactions? Why do your requests open multiple transactions? My quess is that you're doing too many calls from the UI layer to the service layer in a single request.

Outras dicas

Finally i configured my pool the right way... the solution was not to add some c3p0 properties to my hibernate config, i just had to replace my datasource-bean

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
    <!-- Connection properties -->
    <property name="driverClass" value="org.postgresql.Driver" />
    <property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/DBNAME" />
    <property name="user" value="xxx" />
    <property name="password" value="xxx" />
    <!-- Pool properties -->
    <property name="minPoolSize" value="5" />
    <property name="maxPoolSize" value="20" />
    <property name="maxStatements" value="50" />
    <property name="idleConnectionTestPeriod" value="3000" />
    <property name="loginTimeout" value="300" />
</bean>

runs like a charm now

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top