Configurando pool de conexões c3p0 com Spring e Hibernate

Veja neste artigo algumas estratégias de configuração do c3p0, uma biblioteca usada para prover recursos de connection pooling, usando Hibernate e Spring. Aprenda o que deve ser feito para comprovar a efetiva configuração ao projeto.

Configurar um pool de conexões para aplicações é um requisito bastante comum, porém, a aquisição de conexões de banco de dados durante um ciclo de solicitação-resposta (request-reponse) não é uma ideia tão boa quando se deseja que a conexão com o banco leve menos tempo. Tente conectar a um banco de dados no modo de depuração e você vai experimentar um atraso significativo. O pool de conexões vem para resgatar a pré-construção de um número de conexões, com a aplicação os usando, além de ter o retorno da aplicação para opool após o uso. Agora, se você estivesse escrevendo EJBs e implantando-os em um container JEE, aí então o pooling seria fornecido pelo próprio container.

Mas imagine, por exemplo, que você tenha de usar Spring, Hibernate e Tomcat e tenha que gerenciar pools usando algo como c3p0 ou Apache DBCP.

O que você escolheria?

c3p0 é uma biblioteca “easy-to-use” usada para fornecer a capacidade de pooling de conexões. Vide seção Links para referências oficiais do projeto.


Figura 1: Projeto C3P0

Veja na Listagem 1 um exemplo de configuração usando Spring/Hibernate comumente usado por desenvolvedores por aí.

Listagem 1: Configuração Spring/Hibernate

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" /> <!-- ADD PERSISTENCE SUPPORT HERE (jpa, hibernate, etc) --> <!-- HibernateSessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <propertyname="dataSource"> <ref local="dataSource" /> </property> <propertyname="configurationClass"> <value>org.hibernate.cfg.AnnotationConfiguration</value> </property> <propertyname="configLocation"> <value>classpath:hibernate.cfg.xml</value> </property> <propertyname="hibernateProperties"> <props> <propkey="hibernate.show_sql">${hibernate.show_sql}</prop> <propkey="hibernate.dialect">${hibernate.dialect}</prop> <propkey="hibernate.format_sql">${hibernate.format_sql}</prop> <prop key="hibernate.use_sql_comments">${hibernate.use_sql_comments}</prop> <prop key="hibernate.auto_close_session">${hibernate.auto_close_session}</prop> <!-- configuration pool via c3p0 --> <propkey= "hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop> <propkey="hibernate.c3p0.acquire_increment">5</prop> <propkey="hibernate.c3p0.idle_test_period">1800</prop> <propkey="hibernate.c3p0.max_size">600</prop> <propkey="hibernate.c3p0.max_statements">50</prop> <propkey="hibernate.c3p0.min_size">5</prop> <propkey="hibernate.c3p0.timeout">1800</prop> </props> </property> </bean>

O grande problema em tudo isso é que muitos desenvolvedores tentam trabalhar com o c3p0 como um adendo (complemento) ao projeto.

O código da Listagem 1, aparentemente, não funciona. Não há registros específicos do c3p0 no log da aplicação quando do start up da mesma para ter certeza de que o c3p0 foi configurado corretamente. Além disso, imediatamente após o start up, uma rápida verificada no mysql e no número de processos (um por conexão) usando “showprocesslist”; não veio nada.

Mas o que aconteceu? Aparentemente, se você estiver usando Spring e Hibernate, existem duas maneiras que você poderia usar o c3p0. Você pode deixar ou o Spring ou o Hibernate controlar o pool de conexão. Você também pode configurar ambos, mas é redundante e um desperdício de recursos e pode causar problemas com o gerenciamento de transações do Spring. A configuração acima diz que estava tentando fazer com que o Hibernate controlasse o pool, mas tê-lo controlado pelo Spring é o melhor a ser feito.

Hibernate Gerenciando o Pool

Na configuração da Listagem 1, as configurações do c3p0 estão sendo feitas na SessionFactory, o que significa que o Hibernate está sendo responsável pela gestão do pool em vez doSpring.

No caso de o Hibernate querer ser o gerenciador do pool, então a classe org.hibernate.connection.C3P0ConnectionProvider deve servir como o provedor de conexão. Esta classe não se encontra no classpath também. Portanto, se faz necessário adicionar a dependência mostrada na Listagem 2 para incluir a classe acima mencionada.

Listagem 2: Adicionando dependência Maven para classe provider do c3p0

<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>3.6.3.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-commons-annotations</artifactId> <version>3.3.0.ga</version> </dependency>

Isso poderá causar um aumento inicial de logs dos tipos DEBUG e INFO, mas nada tão preocupante.

Spring Gerenciando o Pool

Como mencionado anteriormente, em um ambiente controlado pelo Spring, deixe que o Spring gerencie as conexões ao longo do pool.

A classe de fonte de dados a ser utilizado na configuração acima é org.springframework.jdbc.datasource.DriverManagerDataSource, que de acordo com o javadoc não acumula conexões.

Nota: Esta classe não é um pool de conexão real, de fato ela nem sequer acumula conexões. Ela só serve como substituição simples para um pool de conexão full-blown, implementando a mesma interface padrão, mas criando novas conexões há cada chamada.

Aparentemente, esta classe implementa uma interface que exige o pool de conexão, mas na verdade também não acumula as conexões.

O próximo passo será a inclusão do próprio c3p0 (Listagem 3).

Listagem 3: Adicionando dependência do c3p0

<dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> <type>jar</type> <scope>compile</scope> </dependency>

Então, as configurações de DataSource mudam e ficam conforme exibido na Listagem 4.

Listagem 4: Novas configurações de DataSource

<bean id="dataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <propertyname="driverClass" value="${jdbc.driverClassName}" /> <propertyname="jdbcUrl" value="${jdbc.url}" /> <propertyname="user" value="${jdbc.username}" /> <propertyname="password" value="${jdbc.password}" /> <!-- these are C3P0 properties --> <propertyname="acquireIncrement" value="${c3p0.acquireIncrement}" /> <propertyname="minPoolSize" value="${c3p0.minPoolSize}" /> <propertyname="maxPoolSize" value="${c3p0.maxPoolSize}" /> <propertyname="maxIdleTime" value="${c3p0.maxIdleTime}" /> </bean>

A classe de DataSource a ser utilizada é: com.mchange.v2.c3p0.ComboPooledDataSource

Esta classe é uma c3p0pooleddatasource. A outra diferença de antes é que propriedades do pool são definidas ao nível do DataSource em oposição à fábrica de sessão do Hibernate. O SessionFactoryBean fica bem mais simples, tal como exibido na Listagem 5:

Listagem 5: Novo SessionFactoryBean

org.hibernate.cfg.AnnotationConfiguration classpath:hibernate.cfg.xml ${hibernate.show_sql} ${hibernate.dialect} ${hibernate.format_sql} ${hibernate.use_sql_comments} ${hibernate.auto_close_session}

Concluindo

A partir dessa configuração torna-se clara a exibição da inicialização do c3p0 no Log.

Existem muitas outras configurações envolvendo os ambientes em questão assim como o c3p0. Na seção Links você poderá encontrar referências mais completas para o caso.

Links

Artigos relacionados