Guia do artigo:

Os bancos relacionais são uma realidade já há um bom tempo. Desta forma, é essencial que qualquer linguagem de programação de peso faça acesso a esses bancos. Foi pensando nisso que a Sun Microsystems introduziu a API JDBC (Java Database Connectivity) no JDK. Este artigo tem o objetivo de mostrar o uso desta tecnologia: como consultar bancos de dados, executar alterações e stored procedures, e muito mais.

Como o JDBC funciona?

JDBC é semelhante ao ODBC, e no principio usava justamente ODBC para conectar-se com o banco de dados. A partir de um código nativo as aplicações Java podiam utilizar qualquer banco de dados que tivesse um driver ODBC disponível. Isso contribuiu bastante para a popularização do JDBC uma vez que existe um driver ODBC para praticamente qualquer banco de dados de mercado.

Assim como ODBC, JDBC também funciona através de drivers que são responsáveis pela conexão com o banco e execução das instruções SQL. Esses drivers foram divididos em quatro tipos (ver Figura 1):

  • JDBC tipo 1: foi o primeiro tipo a ser criado, não faz uma conexão real com o banco de dados, mas sim uma conexão com ODBC. Não é muito utilizado hoje em dia por ser escrito em linguagem nativa, o que sacrifica a portabilidade e exige configuração extra no cliente. Ele é composto pelas classes do pacote sun.jdbc.odbc e uma biblioteca de código nativo (não é necessário acessá-la diretamente). Não utilize esse tipo de driver caso tenha outra opção.
  • JDBC tipo 2: esse tipo de driver eliminou a dependência de ODBC, mas ainda é escrito em linguagem nativa, esse código nativo permite fazer chamadas a uma API cliente do SGBD. Também é necessária a instalação de bibliotecas de código nativo na máquina onde o sistema será executado, assim como o tipo 1. Esse tipo de driver também não é portável.
  • JDBC tipo 3: totalmente escrito em Java, eliminou a necessidade de bibliotecas de código nativo favorecendo a portabilidade. Esse tipo de driver permite a conversão de chamadas JDBC em chamadas a um protocolo de rede genérico que então pode ser convertido a chamadas à API específica do SGBD.
  • JDBC tipo 4: também totalmente escrito em Java, utiliza o protocolo de rede proprietário do SGBD, convertendo as chamadas JDBC para chamadas diretas ao SGBD dispensando uma API cliente intermediaria.

Verifique o tipo do driver na documentação do mesmo.

Tipos de drivers JDBC
Figura 1. Tipos de drivers JDBC

Esses drivers são implementações das interfaces do pacote java.sql. Geralmente são disponibilizados na forma de um arquivo JAR (Java ARchive) pelo fabricante do banco de dados ou terceiros. Você pode encontrar drivers JDBC em www.oracle.com, www.dev.mysql.com, www.microsoft.com, www.ibm.com, etc. Ou consultar a base de dados de drivers certificados da Sun em http://developers.sun.com/product/jdbc/drivers.

Após fazer o download do driver, basta adicioná-lo ao CLASSPATH (ler Nota 1). A partir daí está tudo pronto para você acessar o banco de dados via Java, como veremos abaixo.

Nota: A maioria dos IDE’s ignoram a variável de ambiente CLASSPATH e utilizam uma forma própria de gerenciar as classes usadas pelo projeto (nesse caso é necessário adicionar o driver ao projeto).

No eclipse (ver Figura 2): clique com o botão direito no projeto, vá em “Properties”, selecione “Java Build Path” na árvore esquerda, clique na aba “Libraries”, clique no botão “Add External JARs”, selecione o arquivo JAR do driver e clique em “OK”.

Eclipse
Figura 2. Eclipse

Para o NetBeans (ver Figura 3): clique com o botão direito no projeto, vá em “Properties”, selecione “Libraries” na árvore esquerda, clique na aba “Compile”, clique no botão “Add JAR/Folder”, selecione o arquivo JAR do driver e clique em “OK”.

NetBeans
Figura 3. NetBeans

Para outros IDE’s consulte a documentação específica.

Carregando um driver

Obtenha as classes do driver e adicione-as ao CLASSPATH. Nesse artigo utilizaremos o MySQL 5.0, tanto o SGBD quanto o driver podem ser obtidos no site do MySQL e são open-source. Adicione o arquivo JAR ao CLASSPATH como na Figura 4.

Configurando o CLASSPATH
Figura 4. Configurando o CLASSPATH

Como vimos, JDBC é baseada em drivers. Para funcionar, esses drivers precisam estar carregados na memória. Quem gerencia esse carregamento é a classe java.sql.DriverManager: ao ser instanciado, o driver se registra nela e a partir daí já podem ser criadas conexões utilizando-o. Os métodos registerDriver(Driver driver) e deregisterDriver(Driver driver) são utilizados para registrar ou remover o registro de um driver, mas a menos que você esteja desenvolvendo seu próprio driver, não é necessário preocupar-se com esses métodos.

Para carregar um driver utilize o método forName(String name) da classe java.lang.Class. Esse método solicita ao ClassLoader o carregamento da classe especificada por “name”, no nosso caso ficaria assim:


try{

    Class.forName(“com.mysql.jdbc.Driver”);

}catch(ClassNotFoundException cnfe){

    cnfe.printStackTrace();

}

A partir de agora o driver está registrado e já podemos utilizá-lo para abrir uma conexão.

Obtendo uma conexão

Uma conexão é representada pela interface java.sql.Connection. É necessário um objeto de uma classe que implemente essa interface (essa classe é fornecida pelo driver, mas você não precisa se preocupar com ela).

A classe DriverManager possui alguns métodos getConnection(), que são responsáveis por procurar dentre os drivers carregados um que seja compatível com a URL fornecida e solicitar a ele que abra uma conexão.

A URL à qual me referi acima é basicamente o caminho do banco de dados, e tem o seguinte formato:

O protocolo é jdbc, o “subprotocolo” e os parâmetros são específicos do driver e geralmente são especificados na documentação. No caso do MySQL, a URL ficaria como a seguir:


jdbc:mysql://localhost:3306/meu_banco

Onde:

  • jdbc é o protocolo;
  • mysql, o sub-protocolo;
  • //localhost é o endereço do servidor (IP ou nome);
  • 3306 é a porta, que é obrigatória caso não seja a padrão e opcional caso seja, e;
  • meu_banco é nome do banco de dados.

Desta forma, podemos obter uma conexão como apresentado na Listagem 1. O código está dentro de um bloco try porque o método getConnection() pode lançar SQLException. Essa é a exceção lançada pela maioria dos métodos da API JDBC e pode indicar vários problemas na comunicação com o banco de dados, como erros na expressão SQL, falhas de autenticação e outros.

try{

     String url = “jdbc:mysql://localhost/tutorial1”;

     String usuario = “root”;

     String senha = “password”;

     Connection conexao = DriverManager.getConnection(url, usuario, senha);

}catch(SQLException sqle){

     sqle.printStackTrace();

}
Listagem 1. Obtendo uma conexão

O método getConnection() procurará por um driver compatível com o formato da URL passada. Caso não encontre lançará uma exceção:


java.sql.SQLException: No suitable driver.

Caso essa exceção seja lançada no seu código, verifique se o driver foi corretamente carregado. A partir de agora temos aberta uma conexão com o banco de dados e você pode manipulá-la através do objeto Connection criado. Abaixo estão alguns métodos da interface Connection que são mais utilizados:

  • close(): fecha a conexão.
  • commit(): realiza um commit em todas as alterações desde o último commit/rollback. Caso a conexão esteja em modo auto-commit não é necessário chamá-lo explicitamente, pois será executado a cada alteração.
  • createStatement(): um dos métodos mais importantes da conexão, ele cria um objeto Statement que será usado para enviar expressões SQL para o banco. O retorno é um objeto da interface java.sql.Statement.
  • getMetaData(): busca os metadados do banco de dados. Metadados seriam basicamente a estrutura do banco, nomes de tabelas, campos, tipos, etc. Retorna um objeto da interface java.sql.DatabaseMetaData.
  • isClosed(): verifica se a conexão está fechada (retorna true se estiver fechada e false se estiver aberta).
  • isReadOnly(): verifica se a conexão é somente leitura (retorna true se for somente leitura e false se permitir alterações).
  • prepareCall(String sql): cria um objeto para execução de stored procedures, o objeto retornado implementa java.sql.CallableStatement.
  • prepareStatement(String sql): Cria um objeto semelhante ao criado por createStatement(), porém permite trabalhar com queries parametrizadas.
  • rollback(): desfaz as alterações feitas desde o último commit/rollback, é o inverso de commit. Caso a conexão esteja em modo auto-commit não é possível usá-lo, pois a conexão não deixa transações não confirmadas que possam ser desfeitas.
  • setAutoCommit(boolean autoCommit): altera o modo auto-commit da conexão (true para ativar e false para desativar). Caso o auto-commit seja desativado, é necessária a chamada explícita ao método commit(), caso contrário as alterações não terão efeito.

Esses são os métodos mais usados da interface Connection, para conhecer outras possibilidades consulte a documentação da API.

Executando consultas

A execução de consultas e atualizações no banco de dados gira em torno da interface java.sql.Statement (e sub-interfaces). Para criar um objeto desse tipo, utilize os métodos da conexão que está aberta. Por exemplo:

Statment stmt = conexao.createStatement();  //conexao é o nome da variável que criamos acima

Com uma instância de Statment já podemos executar uma query no banco, como abaixo:

ResultSet resultado = stmt.executeQuery(“select * from clientes”);

O método executeQuery() é usado para executar consultas apenas, e não deve ser usado para comandos como update, delete, create, etc. Para isso temos o método executeUpdate() que veremos adiante. Já o método execute() é utilizado em situações em que a query pode retornar mais de um resultado (somente em situações muito particulares ele é utilizado, como em algumas execuções de stored procedures).

O ResultSet

O resultado da consulta está no objeto “resultado” da interface java.sql.ResultSet. Esse objeto possui uma série de métodos getXXX() usados para recuperar os dados que estão no objeto ResultSet de acordo com um tipo. Por exemplo, para recuperar uma coluna do tipo int use o método getInt(), como String getString(), como double getDouble(), e assim por diante.

Um ResultSet controla a posição dos registros retornados utilizando um ponteiro. Esse ponteiro aponta para uma determinada linha, chamada de current, de onde serão retirados os dados ao chamar um dos métodos get. Ao ser criado, esse ponteiro aponta para uma linha antes da primeira válida, e é necessário movê-lo para uma linha válida antes de acessar os dados, caso contrário será lançada a exceção:


java.sql.SQLException: Before start of ResultSet

Para mover esse ponteiro, use um dos métodos de navegação do ResultSet:

  • absolute(int row): move para uma linha específica;
  • afterLast(): move para a linha após a última, ou seja uma linha inválida;
  • beforeFirst(): move para a linha antes da primeira, exatamente como quando o ResultSet é criado, também uma linha inválida;
  • first(): move para a primeira linha;
  • last(): move para a última linha;
  • next(): move para a próxima linha;
  • previous(): move para a linha anterior;
  • relative(int rows): move algumas posições, especificadas pelo parâmetro, relativamente à atual.

No nosso exemplo, podemos ter algo da seguinte forma:

resultado.first();

String nome = resultado.getString(“nome”); //recupera o valor da coluna ‘nome’ como String

String cnpj = resultado.getString(2); //recupera o valor da segunda coluna como String

Date cadastro = resultado.getDate(“cadastro”);

O código da Listagem 2 apresentará todos os registros de uma tabela ‘clientes’ (código, nome, cnpj).

try{

    Class.forName(“com.mysql.jdbc.Driver”); 
    //carrega o driver

    Connection conexao = DriverManager.getConnection(“jdbc:mysql://localhost/meuBanco”, “root”, “senha”); 
    //obtém uma conexão

    Statement stmt = conexao.createStatement(); 
    //cria um Statement

   ResultSet resultado = stmt.executeQuery(“select * from clientes”); 
   //executa uma consulta

 

   while(resultado.next()){ //o método next() retorna true caso haja mais linhas

      System.out.print(resultado.getInt(“codigo”)+”\t”);

      System.out.print(resultado.getString(“nome”)+”\t”);

      System.out.println(resultado.getString(“cnpj”));

   }

 

   resultado.close(); //fecha o ResultSet

   stmt.close(); //fecha o Statement

   conexao.close(); //encerra a conexão

 

}catch(SQLException sqle){

   sqle.printStackTrace();

}catch(Exception e){

   e.printStackTrace();

}
Listagem 2. Exibindo os registros da tabela Clientes

O resultado seria:


1        Jair     00.000.000/0001-00

2        Acme  11.111.111/1111-11

Por padrão, um Statement só permite um ResultSet aberto por vez. Para manter vários ResultSets abertos simultaneamente, eles devem ser criados por Statements diferentes. Por exemplo:


Statement stmt = conexao.createStatement();

ResultSet resultado1 = stmt.executeQuery(“select * from clientes”);

ResultSet resultado2 = stmt.executeQuery(“select * from produtos”);

resultado1.next();

Entretanto, a última linha desse código lançará a seguinte exceção:

java.sql.SQLException: Operation not allowed after ResultSet closed

O primeiro ResultSet foi fechado ao executar o método executeQuery() pela segunda vez, pois implicitamente todos os ResultSets criados pelo mesmo Statement são fechados. O seguinte código resolve o problema:

Statement stmt = conexao.createStatement();

ResultSet resultado1 = stmt.executeQuery(“select * from clientes”);

Statement stmt2 = conexao.createStatement();

ResultSet resultado2 = stmt2.executeQuery(“select * from produtos”);

resultado1.next();

Também pode ser usado o método getMoreResults(int current) para manter os ResultSets abertos.

Statement stmt = conexao.createStatement();

ResultSet resultado1 = stmt.executeQuery(“select * from clientes”);

stmt.getMoreResults(Statement.KEEP_CURRENT_RESULT);

ResultSet resultado2 = stmt.executeQuery(“select * from produtos”);

resultado2.next();

Executando alterações

Como foi dito anteriormente, o método executeQuery() só é válido para consultas. Para executar alterações utilize um dos métodos executeUpdate() disponíveis na interface Statement.

A forma mais simples do método executeUpdate() recebe uma String que é a expressão SQL a ser executada. Essa expressão não pode retornar um resultado e deve ser um INSERT, UPDATE ou DELETE. O retorno do método é um valor int que indica o número de linhas afetadas.

O código da Listagem 3 insere um registro na tabela ‘clientes’ mencionada anteriormente:

try{

    Class.forName(“com.mysql.jdbc.Driver”); //carrega o driver

    Connection conexao = DriverManager.getConnection(“jdbc:mysql://localhost/meuBanco”, 
    “root”, “senha”); //obtém uma conexão

    Statement stmt = conexao.createStatement(); //cria um Statement

    int linhas = stmt.executeUpdate(“insert into clientes (nome, cnpj) values 
    (‘Acme Corporation’, ’00.000.000/0001-00’”); //insere o registro

 

  //caso a conexão estivesse com auto-commit desativado nesse ponto seria 
  necessário chamar conexao.commit();

 

System.out.println(linhas+” linhas afetadas!”);

 

stmt.close(); //fecha o Statement

conexao.close(); //encerra a conexão

 

}catch(SQLException sqle){

   sqle.printStackTrace();

}catch(Exception e){

   e.printStackTrace();

}
Listagem 3. Inserindo registros

A maioria dos bancos de dados possui algum tipo de geração automática para certos campos, (auto_increment no MySQL). Após inserir um registro pode ser necessário obter o valor gerado nesses campos, para isso utilize o método getGeneratedKeys() do Statement (ver Listagem 4).

try{

    Class.forName(“com.mysql.jdbc.Driver”); 
    //carrega o driver

    Connection conexao = DriverManager.getConnection(“jdbc:mysql://localhost/meuBanco”, “root”, “senha”); 
    //obtém uma conexão

    Statement stmt = conexao.createStatement(); 
    //cria um Statement

   stmt.executeUpdate(“insert into clientes (nome, cnpj) values (‘Foo Bar’, ’00.000.000/0001-00’”); 
   //insere o registro

 

 ResultSet res = stmt.getGeneratedKeys(); //obtém as chaves geradas

 

if(res.first()){ //caso o método first() retorne false, não há registros no ResultSet

   System.out.println(“Codigo gerado: “+res.getInt(1));

}else{

   System.out.println(“Nenhum codigo gerado!”);

}

 

stmt.close(); //fecha o Statement

 conexao.close(); //encerra a conexão

 

}catch(SQLException sqle){

   sqle.printStackTrace();

}catch(Exception e){

   e.printStackTrace();

}
Listagem 4. Inserindo e recuperando um valor gerado automaticamente pelo banco para preencher um determinado campo

ResultSet atualizável

É possível utilizar o próprio ResultSet para atualizar os dados na tabela. Para isso é necessário criar o ResultSet como atualizável passando a constante ResultSet.CONCUR_UPDATABLE na criação do Statement que dará origem ao ResultSet:

Statement stmt = conexao.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

ResultSet resultado = stmt.executeQuery(“select * from clientes”);

A partir do objeto ResultSet criado é possível utilizar um dos métodos updateXXX() para alterar valores da linha atual. Por exemplo:

resultado.first();

resultado.updateString(“nome”, “Empresa Ficticia”);

resultado.updateString(“cnpj”, “12.123.123/0001-12”);

resultado.updateRow();

No exemplo acima, o “nome” e o “cnpj” do primeiro registro do ResultSet são alterados na tabela “clientes”.

Outros métodos para atualização dos registros estão disponíveis como updateDouble(), updateInt(), updateDate(), etc. Eles devem ser utilizados de acordo com o tipo de dado que será atribuído ao campo. Os métodos updateXXX() também aceitam o índice da coluna no lugar do nome (resultado.updateString(2, “Empresa Ficticia”) seria o mesmo que o utilizado acima).

Enquanto o método updateRow() é utilizado para confirmar as alterações, o método cancelRowUpdates() pode ser usado para cancelá-las.

resultado.first();

resultado.updateString(“nome”, “Empresa Ficticia”);

resultado.updateString(“cnpj”, “12.123.123/0001-12”);

resultado cancelRowUpdates();

Nesse caso, as alterações feitas pelos métodos updateString() não teriam qualquer efeito no banco de dados.

Também é possível inserir novos registros a partir do ResultSet. Utilizando o método moveToInsertRow() é possível mover-se para uma linha “em branco” do ResultSet, colocar dados nela e utilizar o método insertRow() para gravar as alterações no banco de dados.

resultado.moveToInsertRow();

resultado.updateString(“nome”, “Novo Cliente”);

resultado.updateString(“cnpj”, “55.555.555/0001-55”);

resultado.insertRow();

Caso você desista de inserir o registro e queira voltar à linha normal, utilize o método moveToCurrentRow().

Para excluir uma linha na tabela, pode ser usado o método deleteRow().

resultado.first();

resultado.deleteRow();

Atualizações em batch

É possível utilizar o Statement para enviar atualizações em batch (grupo) para o banco de dados. Para isso, basta adicionar as expressões SQL utilizando o método addBatch() e executá-las com o método executeBatch().

Saiba mais ;)

  • Banco de Dados para Programadores: Todo programador deveria entender de banco de dados para ser um profissional mais completo, mas isso não é tarefa simples. Nesse guia você irá aprofundar seus conhecimentos em SQL, modelagem, e os principais SGBDs do mercado. Vamos evoluir!
  • Guia de Hibernate: Neste Guia de Referência você encontrará cursos, devcasts e artigos que demonstram todos os recursos do Hibernate. Entre eles, é claro, como persistir dados em Java com o framework ORM mais utilizado pelos desenvolvedores.
  • Guia de REST e Java: Devido a sua simplicidade, os Web Services RESTful têm se tornado cada vez mais populares. Neste guia você encontrará os conteúdos que precisa para dominar esse modelo que permite a construção de serviços menores a APIs completas.
Statement stmt = conexao.createStatement();

stmt.addBatch(“insert into clientes values(null, ‘cliente 1’, ’00.000.000/0001-00’”);

stmt.addBatch(“insert into clientes values(null, ‘cliente2’, ’11.111.111/0001-11’”);

stmt.addBatch(“update clientes set nome=’Novo nome’ where codigo=1”);

int[] linhas = stmt.executeBatch();

O retorno do método executeBatch() é um array de int com a quantidade de linhas afetadas em cada expressão executada.

executeBatch() geralmente é executado com o auto-commit desativo, pois caso ocorra um erro em uma das expressões é possível desfazer as alterações (ver Listagem 5). Nesse caso, serão inseridos os dois registros ou nenhum.

try{

    conexao.setAutoCommit(false);

    Statement stmt = conexao.createStatement();

    stmt.addBatch(“insert into clientes values(null, ‘cliente 1’, ’00.000.000/0001-00’”);

    stmt.addBatch(“insert into clientes values(null, ‘cliente2’, ’11.111.111/0001-11’”);

    stmt.executeBatch();

    conexao.commit();

}catch(BatchUpdateException bue){

    conexao.rollback();

}
Listagem 5. Utilizando o executeBatch()

PreparedStatement

Como as expressões SQL são tratadas como strings, para inserir um valor nela é necessário concatenar valores. Isso pode não ser muito conveniente, então podemos usar uma sub-interface de Statement para trabalhar com expressões parametrizadas, o PreparedStatement.

Ao ser criada, a expressão SQL é enviada ao banco e compilada, assim pode ser usada diversas vezes somente mudando os parâmetros, o que pode aumentar significativamente o desempenho.

Um objeto PreparedStatement é obtido através do método prepareStatement(String sql) da conexão, o parâmetro que será passado é a expressão SQL e no lugar de cada parâmetro um ?:

PreparedStatement stmt = conexao.preapareStatement(“insert into amigos 
(nome, telefone) values (?, ?)”);

A partir desse objeto é possível inserir vários registros na tabela ‘amigos’ simplesmente setando os parâmetros:

stmt.setString(1, “João”);

stmt.setString(2, “(31) 5555-5555”);

stmt.executeUpdate();

stmt.setString(1, “Ana”);

stmt.setString(2, “(31) 1111-1111”);

stmt.executeUpdate();

Veja que não é necessário recriar o objeto para executar outra alteração, os dados dos parâmetros são retidos no objeto até que sejam substituídos por outros, ou pela chamada do método clearParameters().

stmt.setString(1, “José”);

stmt.setString(2, “(31) 9999-9999”);

stmt.executeUpdate();

stmt.clearParameters();

stmt.setString(1, “Maria”);

stmt.setString(2, “(11) 1234-1234”);

stmt.executeUpdate();

Há uma série de métodos setXXX() na interface PreparedStatement, e cada um recebe um tipo de parâmetro, sempre o índice do parâmetro e o valor a ser colocado nele, de acordo com o método:

stmt.setInt(1, 25);

stmt.setDouble(2, 1.35);

stmt.setString(3, “teste”);

Deve ser definido um valor para todos os parâmetros antes de efetuar a execução, caso contrário será lançada a seguinte exceção:

java.sql.SQLException: No value specified for parameter 2

O uso de preparedStatement considerando o código da Listagem 3 e as devidas alterações pode ser visto na Listagem 6.

try{

    Class.forName(“com.mysql.jdbc.Driver”); //carrega o driver

    Connection conexao = DriverManager.getConnection(“jdbc:mysql://localhost/meuBanco”, 
    “root”, “senha”); //obtém uma conexão

    PreparedStatement stmt = conexao.prepareStatement(“insert into clientes 
    (nome, cnpj) values (?, ?)”); //cria um PreparedStatement

  

   //seta os parâmetros

   stmt.setString(1, “Acme Corporation”);

   stmt.setString(2, “00.000.000/0001-00”);

   //executa

   int linhas = stmt.executeUpdate();

 

  //caso a conexão estivesse com auto-commit desativado nesse ponto seria necessário chamar

  conexao.commit();

 

 System.out.println(linhas+” linhas afetadas!”);

 

 stmt.close(); //fecha o Statement

 conexao.close(); //encerra a conexão

 

}catch(SQLException sqle){

   sqle.printStackTrace();

}catch(Exception e){

   e.printStackTrace();

}
Listagem 6. Utilizando o PreparedStatement

Stored procedures

A chamada de stored procedures também é possível com JDBC. A interface base para isso é java.sql.CallableStatement (sub-interface de PreparedStatement).

Assim como um Statement ou PreparedStatement, um CallableStatement é criado a partir de uma conexão ativa através do método prepareCall(String sql). A String passada como parâmetro tem uma sintaxe particular e padronizada, o driver é responsável por convertê-la para a sintaxe especifica do SGBD utilizado.

Para exemplificar, vamos emitir a listagem de clientes utilizando uma stored procedure. O procedimento seria criado conforme Listagem 7 no banco.

delimiter //

create procedure sp_listarClientes()

begin

     select * from clientes;

end

//
Listagem 7. Criando o procedimento de seleção de clientes no banco de dados

A sintaxe para chamar o procedimento é {call ()}. No exemplo ficaria como apresentado na Listagem 8.

try{

    CallableStatement chamada = conexao.prepareCall(“{call sp_listarClientes()}”); //monta a chamada

    ResultSet resultado = chamada.executeQuery(); //executa

 

     //lista o resultado

     while(resultado.next()){

        System.out.print(resultado.getInt(“codigo”)+”\t”);

         System.out.print(resultado.getString(“nome”)+”\t”);

         System.out.println(resultado.getString(“cnpj”));

      }

 

      resultado.close(); //fecha o ResultSet

      chamada.close(); //fecha o Statement

      conexao.close(); //encerra a conexão

}catch(Exception e){

     e.printStackTrace();

}
Listagem 8. Chamando o procedimento de seleção de clientes

Um procedimento para inserir um novo cliente pode ser visto na Listagem 9.

delimiter //

create procedure sp_inserirCliente(pNome varchar(50), pCnpj varchar(50))

begin

     insert into clientes(nome, cnpj) values (pNome, pCpnj);

end

//
Listagem 9. Criando o procedimento de inserção de cliente no banco de dados

A utilização da stored procedure criada na Listagem 9 pode ser vista na Listagem 10.

try{

    CallableStatement chamada = conexao.prepareCall(“{call sp_inserirCliente(?,?)}”); 
    //monta a chamada

    chamada.setString(1, “SQL Magazine”);

    chamada.setString(2, “99.999.999/0001-99”);

    chamada.executeUpdate(); //executa

  

    chamada.close(); //fecha o Statement

    conexao.close(); //encerra a conexão

}catch(Exception e){

     e.printStackTrace();

}
Listagem 10. Chamando o procedimento de inserção

Procedimentos com parâmetros de saída devem registrar esses parâmetros chamando um dos métodos registerOutParameter(). A forma mais simples desse método recebe dois parâmetros int, o primeiro é o índice do parâmetro e o segundo o tipo SQL do retorno, os tipos estão definidos por atributos estáticos da classe java.sql.Types.

Seguindo nosso exemplo, imagine que queiramos saber a quantidade de clientes cadastrados. Para isso utilizaremos o procedimento da Listagem 11.

delimiter //

CREATE PROCEDURE sp_contagemClientes(OUT total INT)

BEGIN

    SELECT COUNT(*) into total from clientes;

END

//
Listagem 11. Criando o procedimento de contagem de registros no banco de dados

Para obter o valor de saída devem ser usados os métodos getXXX() da interface CallableStatement, semelhantes aos de um ResultSet como pode ser visto na Listagem 12.

try{

    CallableStatement chamada = conexao.prepareCall(“{call sp_contagemClientes(?)}”); 
    //monta a chamada

    chamada.registerOutParameter(1, java.sql.Types.INTEGER); 
    //registra o parâmetro de saída como INTEGER

    chamada.executeQuery(); //executa

    int total = chamada.getInt(1);

 

    System.out.println(“Total de clientes: “+total);

  

    chamada.close();

    conexao.close();

}catch(Exception e){

     e.printStackTrace();

}
Listagem 12. Executando o procedimento de contagem de registros

Conclusão

JDBC é uma tecnologia madura e completa para acesso a dados em Java. Mesmo que os frameworks de persistência como o Hibernate sejam os preferidos para persistir dados em Java, todos eles utilizam internamente JDBC, o que torna essencial a compreensão do funcionamento dessa importante API.

Saiu na DevMedia!

  • O que é PSR?: Neste curso aprenderemos o que é PSR, recomendações de padrões de codificação para a linguagem PHP. A partir da aplicação destes padrões, podemos aproximar a forma como o código é escrito por diferentes equipes de desenvolvimento, facilitando a colaboração entre elas.
  • Linguagem R: elaborando gráficos com Plotly: Este artigo abordará um dos recursos existentes para elaborar gráficos utilizando a linguagem de programação R.
  • Laravel e Eloquent ORM: API RESTful com relacionamento 1:N: Neste curso veremos como implementar o relacionamento 1:N em APIs utilizando o Laravel. Partiremos de uma API de empresas pronta, ao longo do curso, veremos como relacionar com uma entidade de vagas e posteriormente com uma entidade de requisitos.

Saiba mais sobre PHP ;)

  • Qualidade no código Java com boas práticas e Clean Code: Manter um código limpo tem um impacto muito relevante na qualidade e na facilidade de manutenção de um sistema. Este artigo demonstrará a importância que deve ser dada em manter o código da sua aplicação limpo.
  • JWT: Web services seguros em Java: Aprenda a programar web services RESTful seguros utilizando JWT (JSON Web Tokens). Para isso tomaremos como base uma Web API que já fornece um CRUD de marcas e produtos, mas que ainda não provê nenhum mecanismo de segurança, nenhum controle de autenticação e autorização.
  • API RESTful em Java: subrecursos em um relacionamento N:N: Aprenda a implementar uma Web API RESTful em sistemas que possuam relacionamentos N:N, isto é, do tipo muitos para muitos, no banco de dados.