Apesar de ser bem conhecido, muitas pessoas não sabem configurar ou nem sabem que existe algo muito interessante a respeito de conexão com Banco de Dados com Java Web: é o conceito de DataSources (ou Connection Pool). Utilizando este tipo de conexão, há uma otimização violenta na utilização de qualquer banco de dados. Neste post, irei descrever os benefícios de utilizar Datasources e de como utilizar tal recurso no Tomcat. É recomendado que o leitor saiba o que são conexões e drivers JDBC e como utilizá-los.
Qualquer aplicação web deve estar pronta para receber várias requisições simultâneas, e em cada uma delas normalmente se acessa um banco de dados para se obter o conteúdo da página. A cada requisição, é aberta e fechada uma conexão com o Banco, um processo que causa certa lentidão, pois muitas requisições simultâneas requerem que múltiplas conexões sejam criadas.
A tradução de Connection Pool é “piscina de conexões”, e é justamente essa a idéia que se deve ter: um local onde já existe uma ou mais conexões com o banco de dados ociosas, esperando que uma requisição seja feita.
Ta… mas qual a vantagem de usar este pool?
A vantagem é que várias requisições podem ser atendidas por um número pequeno de conexões, pois após as mesmas serem utilizadas, elas voltam para o Pool, e ficam prontas para atender uma outra requisição.
Sem contar o ganho de tempo, pois a conexão já está estabelecida: não é necessário efetuar todo o processo de login e estabelecer uma nova conexão no banco para cada solicitação.
Hmmmm… tá ficando bom! E o que eu preciso para utilizar isso?
Precisa configurar o conteiner web para que o mesmo possa gerenciar o Pool. Neste post, explicarei como fazer esta configuração no Tomcat.
Moral do Pool de Conexões
O pool nada mais é que um conjunto de conexões JDBC esperando para atenderem alguma solicitação. Sendo assim, o pool funcionará da seguinte forma:
- caso seja solicitada uma conexão e não houver nenhuma no pool, uma conexão será criada para atender esta solicitação. Enquanto ela estiver atendendo esta requisição, ficará com o status de ‘ocupada’, e após atendê-la, esta conexão voltará para o pool com o status de ‘ociosa’;
- caso seja solicitada uma conexão e todas as existentes no pool estiverem com status de ‘ocupada’ uma nova conexão será criada (respeitando o limite máximo de conexões do pool);
- caso o numero máximo de conexões for atingido e todas estiverem ocupadas, o pool irá aguardar uma conexão retornar a ficar ociosa para atender a uma requisição.
Desta forma, percebemos que o numero máximo de conexões dentro de um pool é o mesmo de requisições simultãneas que ocorrerem.
Passos para a utilização de DataSources no Tomcat:
1 – Salvar o driver de conexão com o banco de dados no diretório de libs do Tomcat ($CATALINA_HOME/lib);
2 – Configurar no Contexto da aplicação no Tomcat os seguintes dados (exemplo de conexão com o Banco POSTGRESQL):
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/exemplo" docBase="exemplo">
<!-- datasource -->
<Resource name="jdbc/exemplo-ds"
auth="Container"
scope="Shareable"
type="javax.sql.DataSource"
username="usuario_banco"
password="senha_banco"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/nome_banco"
maxActive="100"
maxIdle="30"
maxWait="10000"/>
</Context>
Para quem não sabe configurar um elemento de contexto no Tomcat, uma breve ajuda de onde o mesmo pode ser definido:
a) em $CATALINA_HOME/conf/context.xml: será carregado por todas as aplicações do container;
b) em $CATALINA_HOME/conf/[enginename]/[hostname]/context.xml: será carregado por todas as aplicações do hostname especificado;
c) no diretório $CATALINA_HOME/conf/[enginename]/[hostname]/”nomedocontexto.xml”. Será carregado pela aplicação que tiver o nome do contexto igual ao nome do XML (sem a extensão);
d) SE NENHUM DOS ARQUIVOS ACIMA existir, em /META-INF/context.xml dentro da própria aplicação.
Um detalhe que não pode deixar passar despercebido é que, caso a opção D seja utilizada, o arquivo context.xml dentro do META-INF será copiado para o $CATALINA_HOME/conf/[enginename]/[hostname]/”nomedocontexto.xml”. Neste caso. ao fazer uma alteração no context.xml, deve-se entrar no diretório $CATALINA_HOME/conf/[enginename]/[hostname]/ e deletar o “nomedocontexto.xml”, caso contrário o arquivo não será atualizado (sofri alguns dias até descobrir isso!).
3 – Incluir no web.xml o recurso JNDI a ser usado:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<!-- dados do seu web.xml .... -->
<resource-ref>
<res-ref-name>jdbc/exemplo-ds</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
4 – Criando uma conexão pelo Pool no código Java:
InitialContext initCtx = new InitialContext(); DataSource ds = (DataSource) initCtx.lookup( "java:/comp/env/jdbc/exemplo-ds" ); Connection con = ds.getConnection();
E é isso!
Um detalhe IMPORTANTÍSSIMO é fechar TODAS as conexões utilizadas pelo pool, pois na verdade a conexão não será “fechada”, e sim devolvida ao Pool. É como pegar um livro de uma biblioteca: ele não voltará para a prateleira sozinho: você tem que devolvê-lo. E devolver a conexão para o pool é utilizar o método close() do objeto Connection. Abaixo um exemplo de código:
Connection conn = null;
Statement stmt = null; // Ou PreparedStatement
ResultSet rs = null;
try {
conn = //... pega a conexão do pool ...
stmt = conn.createStatement("select ...");
rs = stmt.executeQuery();
//... itera sobre o resultset e cria os objetos ...
} catch (SQLException e) {
//... trata os erros ...
} finally {
// fecha todos os recursos e devolve a conexao ao pool
if (rs != null && !rs.isClosed() ) {
try { rs.close(); } catch (SQLException e) { ; }
rs = null;
}
if (stmt != null && !stmt.isClosed() ) {
try { stmt.close(); } catch (SQLException e) { ; }
stmt = null;
}
if (conn != null && !conn.isClosed() ) {
try { conn.close(); } catch (SQLException e) { ; }
conn = null;
}
}
Bom, é isso! Em um próximo post irei explicar como funciona o uso de DataSources no JBoss.
Espero ter ajudado.
Referências:
http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html
http://tomcat.apache.org/tomcat-5.5-doc/config/context.html
Abraço!
muito bom, nada mais a acresentar…
Excelente!
Descobri vários detalhes que eu desconhecia…
Parabéns!
Olá Hallan,
Segui seu tutorial contudo minha aplicação não está executando no IE nem na IDE (eclipse Galileo), somente no Chrome
Alguma idéia?!?!
Desde já agradeço a atenção!
Jeverson:
Qual o erro que você teve?
Se está funcionando no Chrome, então o problema não deve estar no seu código, e sim no IE/Navegador do Eclipse.
Normalmente, quando funciona em um browser e no outro não, é por conta do cache. Tente limpar o cache e acessar no IE novamente.
Abraço!
Limpei o cache, porém o erro persiste. No IE7 ele fica somente na index.html e tentando conectar, o msmo ocorre no browser da IDE.
Meu bean:
package com.corejsf;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.jsp.jstl.sql.Result;
import javax.servlet.jsp.jstl.sql.ResultSupport;
import javax.sql.DataSource;
public class CustomerBean {
private Connection conn;
public void open() throws SQLException, NamingException {
if (conn != null) return;
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(“java:comp/env/jdbc/mydb”);
conn = ds.getConnection();
}
public Result getAll() throws SQLException, NamingException {
try {
open();
Statement stmt = conn.createStatement();
ResultSet result = stmt.executeQuery(“SELECT * FROM Customers”);
return ResultSupport.toResult(result);
} finally {
close();
}
}
public void close() throws SQLException {
if (conn == null) return;
conn.close();
conn = null;
}
}
O web.xml da minha aplicação:
05f_database
Faces Servlet
javax.faces.webapp.FacesServlet
1
Faces Servlet
*.faces
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
MySQL DataSource
jdbc/mydb
javax.sql.DataSource
Container
a index.jsp:
index.html:
Iniciando aplicação web
Aguarde enquanto a aplicação web é iniciada!
${catalina_home}/conf/context.xml:
WEB-INF/web.xml
<!–
–>
<!–
–>
Seguindo outro tutorial, também acrescentei à tag do meu ${catalina_home}/conf/server.xml o seguinte:
Alguma dica de onde possa estar o erro??
Caso tenha postado indevidamente aqui, desculpe-me!
Grato pela atenção
Abraço!
hummm….seu blog ñ aceita as tags…rs!
Neste tópico do GUJ tem uma msg q postei sobre o assunto:
http://www.guj.com.br/posts/list/141523.java#768777
Assim:
Estás usando JSF também. Eu recomendo você testar uma coisa de cada vez, para achar a fonte do problema. Tente substituir (comentar) os códigos de acesso ao BD por objetos criados “na hora”: assim você sabe se o problema é o acesso ao BD ou não.
Como eu disse: se funciona do Chrome, a conexão funciona, pois ela independe de browser (roda no servidor). Já o JSF roda no browser – provável causa do problema.
Lembro que o JSF é assim mesmo: depende do browser, do servidor (tomcat/jboss/glassfish/websphere) – cada um deles tem suas particularidades, o que pode complicar sua configuração do faces.
Abraço!
Então…estou estudando JSF pelo livro CoreJSF, venho seguindo capítulo a capítulo, sendo assim, jah testei várias aplicações JSF em meu ambiente de desenvolvimento, todas rodaram numa boa! Vou testar agora com o exemplo do jndi-datasource-examples-howto da documentação do TomCat6 e também vou instalar o FireFox pra ver qual o resultado obtido. Qlq novidade posto aki!
Vlw pela atenção (vou ajudar a divulgar seu blog…hehe!)
abraço
Caro Hallan e d+ leitores,
Problema resolvido…..
O lance era nessa linha do meu index.html
meta http-equiv=”Refresh” content=”0; index.faces”
o correto é isso:
meta http-equiv=”Refresh” content=”0; URL=index.faces”
sem o URL o IE não redireciona, jah o Chrome redireciona numa boa!
Abraço a todos!
Cara, massa você ter conseguido resolver!
Programar pra web eh assim mesmo: dias pra descobrir que um browser tem “facilidades” que outros não tem hehee!!
Abraço e sucesso!
ÊÊÊÊÊÊÊÊÊÊÊÊ, até que enfim um tutorial que funcionou!!!!!
Gostei do post …
VALEUUUU
Parabéns!….muito bom seu blog.
tenho uma pergunta quanto ao pool:
usando o pool não há possibilidade de causar duplicidade de registro, ja que as conecções não fecham?
Não há essa possibilidade, pois a conexão só volta para o Pool depois de ser utilizada e “devolvida explicitamente” pela aplicação. Em nenhum momento uma conexão será usada simultaneamente em dois lugares.
Abraço!