Banco de Dados


JDBC é uma API escrita em Java que serve como uma ponte entre os programas e o banco de dados. Cada banco de dados possui uma implementação JDBC chamada de driver.

JDBC é um conjunto de classes e interfaces em Java, que proporcionam uma interface similar a ODBC para bases de dados SQL.

Pode-se usar JDBC, de dentro de programas Java, para acessar quase todos as bases de dados SQL, incluindo ORACLE 7, SYBASE, DB2, SQL SERVER, ACCESS, FOXBASE, PARADOX, PROGRESS, SQLBase e XDR. Drivers JDBC estão disponíveis em Symantec, Intersolv, IBM, JavaSoft, e Inprise (Borland e Visigenic).

Arquitetura básica da API JDBC:

Baseada em ODBC
ODBC é a parte do banco de dados do Microsoft Windows Open Services Architecture (WOSA), que é uma interface que permite que os aplicativos de desktop baseados em Windows para se conectar a vários ambientes de computação sem reescrever o aplicativo para cada plataforma. É uma API (Application Programming Interface) que funciona como driver entre uma aplicação e base de dados.
- Usando JDBC, pode-se obter acesso direto a bancos de dados através de applets e outras aplicações Java

JDBC é uma interface de nível de código
- Código SQL é usado explicitamente dentro do código Java
- O pacote java.sql consiste de um conjunto de classes e interfaces que permitem embutir código SQL em métodos.
- Com JDBC é possível construir uma aplicação Java para acesso a qualquer banco de dados SQL.
- O banco deve ter pelo menos um driver ODBC, se não tiver driver JDBC.
- Para usar JDBC é preciso ter um driver JDBC.
- O J2SE distribui um driver ODBC que permite o acesso a bancos que não suportam JDBC mas suportam ODBC.

Classes JDBC para criar Conexão
java.sql.DriverManager
DriverManager é uma classe. Sua principal função é manter uma lista de drivers de diferentes fornecedores e associar uma aplicação-cliente com um driver que corresponda ao URL requerido.

java.sql.Connection
A classe Connection representa uma única transação da base dados. Você usa Connection para enviar uma serie de statements SQL  à base de dados e gerenciar a realização completa da transação (commiting) ou o aborto dos statements da mesma (aborting).

java.sql.SQLException
A classe SQLException herda da classe geral java.lang.Exception, que provê informação extra sobre erros nos acessos à base de dados.
Se ocorrer diversos erros durante a execução de uma transação, pode-se encadeá-los todos eles juntos nesta classe.  Isto é frequentemente útil quando tem-se exceções que deseja-se permitir que o usuário saiba, mas não deseja-se parar o processamento.

java.sql.Statement
A classe Statement é a mais básica das três classes JDBC representando statements SQL. Ela realiza todas as statements SQL básicas. Em geral, uma simples transação usa somente um dos três métodos de execução de statements na classe Statement. O primeiro método, executeQuery(), toma um string SQL como um argumento e retorna um objeto da classe Result. Este método deve ser usado para qualquer chamada SQL que espera retornar dados da base de dados. Statements “UpDate” são executadas usando o método executeUpDate(). Este método retorna o número de linhas afetadas na base de dados. 
statement (
ˈsteɪtmənt): s. 1. declaração, to make /issue a statement: fazer/dar uma declaração, 2. extrato (bancário).

java.sql.Result
Result é uma classe da qual um objeto é instanciado, representando uma linha de dados retornada por uma consulta à base de dados. A classe Result provê uma série de métodos para recuperar colunas dos resultados de uma consulta à base de dados.

Pacote Java padrão: java.sql

Categoria
Classe ou Interface
Fornecido por
Comentario
Mapeamento entre tipos de dados
SQL e tipos de dados Java.
Classe Date
Classe Time
Classe TimeStamp
java.sql
java.sql
java.sql
Valor SQL para data.
Valor SQL para horario.
Valor SQL para “TimeStamp”.
Fornecimento de metadados sobre o banco de dados.
Interface DatabaseMetadata.
Interface ResultSetMetadata.
Fabricante do SGBD.
Fabricante do SGBD.
Informacão sobre o banco de dados.
Objeto empacotador.
Colunas ResultSet.
Objeto empacotador de informacão.
Geracao de Excecões
Classe SQLException.
Classe SQLWarning.
java.sql
java.sql
Erros de acesso ao banco de dados.
Alertas de acesso ao banco de dados.

Descrição das interfaces do java.sql

Categoria
Componente
Funcionalidade
Carregar um driver
java.sql.DriverManager
Gerencia um conjunto de drivers
Representa um driver e cria uma
conexão
java.sql.Driver
Fornece uma conexão e informações do driver. Cada driver é representado por uma URL.
Representa a conexão
java.sql.Connection
Representa uma conexão ao banco e uma sessão
Executa um comando SQL estático
java.sql.Statement
Executar comandos SQL

java.sql.PreparedStatement
Executar comandos pré-compilados e facilitar o mapeamento Objeto-Relacional

java.sql.CallableStatement
Executar stored procedures (funções criadas e armazenadas dentro do banco de dados
Recuperar dados resultantes de um comando SELECT
java.sql.ResultSet
Fornecer, dentro da aplicação, uma lista que funcione como um espelho das informações contidas no banco de dados resultante da execução de um SELECT
Exceções
java.sql.Exception
Representar erros durante a execução dos comandos SQL


As principais classes e interfaces do pacote java.sql

Cada fabricante de drive de conexão fornecerá uma implementação para as classes:

1. DriverManager - classe que gerencia o driver e cria uma conexão com o banco.

2. Connection - é a classe que representa a conexão com o bando de dados.
A interface Connection possui os métodos para criar um Statement, fazer o commit ou rollback de uma transação, verificar se o auto commit está ligado e poder (des)ligá-lo, etc.

3. Statement - controla e executa uma instrução SQL .
As interfaces Statement e PreparedStatement possuem métodos para executar comandos SQL.

4. PreparedStatement - controla e executa uma instrução SQL. É melhor que Statement.

5. ResultSet - contém o conjunto de dados retornado por uma consulta SQL.
ResultSet possui método para recuperar os dados resultantes de uma consulta, além de retornar os metadados da consulta.

6. ResultsetMetaData - é a classe que trata dos metadados do banco.
ResultsetMetaData possui métodos para recuperar as meta informações do banco.


Gerando Instruções de Atualização

Um determinado grupo de instruções SQL pode ser considerado como de atualização, no sentido de que não se deve esperar que retornem linhas de tabelas. Se encaixam neste gruopo as instruções de CREATE TABLE, INSERT, UPDATE e DELETE.

JDBC fornece um método para enviar instruções de atualização sem esperar por resultados. O objeto Statement, que é fornecido pelo objeto Connection, provê  o método  executeUpDate (). Para executá-lo é preciso obter antes um objeto Statement. Depois de feito uma conexão bem sucedida, pode-se recuperar um objeto Statement com uma linha:

Statement  stmt  =  connection.createStatement ();
Então, pode-se  executar instruções de atualização:
stmt.executeUpDate ( “CREATE TABLE  .... ” );
stmt.executeUpDate ( “INSERT INTO ...  ” );

stmt.close ();

Quando terminar de usar o objeto Satement, ele deve ser fechado como uma conexão.
stmt.close ();

O acesso básico inicia com o objeto Connection já criado antes. Quando este objeto é criado, ele é uma simples ligação direta à base de dados. Usa-se um objeto Connection para gerar implementações de java.sql.statements vinculadas a uma mesma transição.

Após ter usado uma ou mais objetos da classe Statement gerados pelo objeto Connection, pode-se usar ela para fazer  commit ou rollback com os objetos statements associados com a Connection.

commit (kəˈmɪt): v. 1. cometer (um crime, um pecado), 2.  obrigar, to commit yourself (to doing sth) comprometer-se (a fazer algo), 3.  destinar (dinheiro, recursos).

roll.back: 1. redução de preços (por tabelamento); 2. repetição; 3; recuo.

Um objeto Statement é uma declaração SQL. Uma vez que  tenha-se obtido um objeto Connection, tem-se os campos de como se fosse um “cheque em branco” que pode-se preencher para a transação representada pelo objeto Connection.

JDBC usa um método diferente para enviar consultas, em relação a atualizações. A diferença chave é o fato que o método para consultas retorna uma instância de java.sql.Result, enquanto o método de não-consultas retorna um inteiro. A classe Result provê ao programador com acesso aos dados recuperados pela consulta.


Transação

Uma transação é um conjunto de operações, delimitadas por um início e um fim. Iniciando quando se executa o primeiro comando SQL e terminando de acordo com as seguintes situações:


Commit Comando que grava definitivamente os efeitos dos comandos de uma transação (insert, delete e update)
Rollback Comando que desfaz os efeitos dos comandos da transação (insert, delete e update)
Fim da sessão
Quando a sessão é encerrada sem problemas, ocorre um commit implícito, caso haja algum problema, ocorre um rollback implícito.
Comando DDL ou DCL
Todo comando DDL (create, alter e drop) e DCL (grant e revoke) provocam o fim da transação corrente, havendo um commit implícito.

Por default, JDBC realiza (commits) cada statement SQL quando ela é enviada à base de dados; isto é chamado autocommit. Contudo, uma manipulação mais robusta de erros, você pode estabelecer um objeto Connection que emite uma série de mudanças que não têm nenhum efeito sobre a base de dados, até que você expressamente envie um commit. Cada Connection é separada, e um commit sobre ela não tem nenhum efeito sobre o statement da outra. A classe Connection provê o método setAutoComit(), assim pode-se tornar o autocommit desligado (off).

Esquema de uma interação com um banco de dados:


Acessar dados com JDBC
Para a aplicação Java se comunicar com um banco de dados e acessar os seus dados, uma conexão com o Banco de Dados deve ser estabelecida.

A conexão é estabelecida da seguinte forma:

1. Carregamento na memória do driver JDBC de acordo com o banco de dados:
Para o MySQL:

Class.forName("com.mysql.jdbc.Driver");

Para o Acess:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Para o Oracle:

Class.forName("oracle.jdbc.driver.OracleDriver");

Para o Microsoft SQL Server 2000:

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

Para o Microsoft SQL Server 2005/2008:

Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

Quando se utiliza o método estático Class.forName(), o Class Loader tenta inicializar esta classe. Esta classe (que é o driver jdbc) ela possui um bloco inicializador estático, que irá registrar essa classe como um driver JDBC, avisando o java.sql.DriverManager, pelo método registerDriver.


A classe Class tem como função instanciar a classe identificada entre parênteses e registrá-la com a JVM, que irá utilizá-la para acessar o SGBD (Sistema de Gerenciamento de Banco de Dados). Após o registro do driver, pode-se abrir a conexão.


Até a versão 3 do JDBC, antes de chamar o DriverManager.getConnection() era necessário registrar o driver JDBC que iria ser utilizado através do método Class.forName("com.mysql.jdbc.Driver"), no caso do MySQL, que carregava essa classe, e essa se comunicava com o DriverManager. A partir do JDBC 4, que está presente no Java 6, esse passo não é mais necessário.

1.1. Abrir conexão com do BD

String url = "jdbc:mysql://localhost:porta/nomebanco"
Connection con = DriverManager.getConnection(“url”,“usuario”,“senha”);

1.1.1. url: jdbc:mysql://localhost:porta/nomebanco

- Sempre inicia com jdbc, o resto depende do fabricante do banco de dados.
- Na seqüência normalmente tem-se o nome do fabricante (jdbc:db2, jdbc:oracle, jdbc:mysql, etc)
- Caminho da fonte de dados. Trata-se do path em que a fonte de dados está localizada. Quando o arquivo é local, o endereço não é obrigatório. Normalmente o caminho inclui nome de servidor, como, por exemplo, //localhost ou //meu-servidor.

Por último o nome da fonte de dados ou caminho da fonte de dados. A fonte de dados, para a maioria dos SGBD, representa uma tabela (com as visões dentro dela).


1.1.2. usuario: usuário do banco de dados
1.1.3. senha
: do banco de dados
O objeto con representa uma conexão ativa e aberta ao banco de dado a partir da conexão pode-se enviar statements SQL para o banco.
1.1.4. Adição do driver de conecção no projeto
Para o MYSQL o drive de conecção é o arquivo mysql-connector-java-5.1.23-bin:
O MySQL JDBC Driver já vem com o Netbeans, para inserí-lo no projeto basta dar um clique com o botão direito na pasta Biblioteca do respectivo projeto do Netbeans, clique em Adicionar biblioteca..., escolha o drive para o MySQL.

Para quem utiliza o eclipse, caso o mesmo não possua esse driver, o download pode ser feito direto pelo site, http://dev.mysql.com/downloads/connector/j/ ou apenas copiá-lo do Netbeans localizada na pasta ...\ide\modules\ext\mysql-connector-java-5.1.23-bin e adicioná-lo ao projeto.

1.1.5. Adicionando o driver de conecção no JAVA (JDK)

deve-se adicionar o seu caminho à variável de ambiente CLASSPATH, ou então copiar o driver na pasta ext do JDK (C:\java\jre7\lib\ext), da mesma forma para o jre em tempo de desenvolvimento (C:\java\jdk17025\jre\lib\ext).


Obs: java é a pasta onde foi instalado o JAVA (JDK), esta instalação já foi comentada na apostila I.


Exemplo: é mais prático criar duas constante, uma para o Driver e outra para o banco de dados (a assinatura do método fica menor).

Classe Conectar.java

import java.sql.*;
import javax.swing.*;

public class Conectar {
    public static void main(String[] args) {
        final String DRIVER = "com.mysql.jdbc.Driver";
        final String URL = "jdbc:mysql://localhost:3306/bancodados";
        try {
            Class.forName(DRIVER);
            Connection connection = DriverManager.getConnection(URL, "root", "2345");
            JOptionPane.showMessageDialog(null, "Conexao realizada com sucesso");
            connection.close();
        }
        catch (ClassNotFoundException erro) {
            JOptionPane.showMessageDialog(null, "Driver nao encontrado!\n" erro.toString());
        }
        catch (SQLException erro) {
            JOptionPane.showMessageDialog(null, "Problemas na conexao com a fonte de dados\n" erro.toString());
        }
    }
}

O java.sql.DriverManager vai perguntar para cada Driver registrado, se ele aceita a String de conexão em questão. Se algum deles aceita esta String, a conexão é aberta (pelo Driver), e retornada. Caso contrário, uma exceção é lançada. Pela própria String de conexão, você pode passar muitos outros parâmetros (além de login, senha e nome do banco de dados, como neste caso), a maioria de interesse do Driver, como por exemplo saber se aquela conexão deve estar em modo de auto reconexão caso ela caia, e muitos outros. Este tipo de informação está na documentação do seu driver.

A partir da conexão é possível interagir com o banco de dados, fazendo consultas, atualização e busca às meta informações da base e das tabelas.

Após o carregamento do driver, a classe DriverManager é a responsável por se conectar ao banco de dados e devolver um objeto Connection, que representa a conexão com o BD.


Fechamento de uma Conexão: o Método Close()

As classes Connection, Statement, e Result têm o método close(), para fechamento da conexão. Uma dada implementação JDBC permitirá ou não, requerer o fechamento da conexão antes que alguns objetos sejam reutilizados. Pode acontecer de existir um objeto que seja bom  não ser destruído. Ele poderá estar retendo recursos importantes da base de dados. É, portanto, sempre uma boa idéia fechar qualquer instância desses objetos, quando você já tiver feito tudo com eles.


Exemplo: teste de conexão

//Todos os imports necessários para conexão
import java.sql.Connection;
import java.sql.DriverManager;
import javax.swing.JOptionPane;

public class TestaConexao {
    //Aqui colocamos o endereço do banco de dados e o nome do banco.
    private String url = "jdbc:mysql://localhost:3306/agendadb";
    //Usuario do banco
    private String usuario = "root";
    //Senha do banco de Dados
    private String senha = "";
    
    private Connection con;    

   //Metodo que fará a conexão
    public boolean conectar(){
        try{
            Class.forName("com.mysql.jdbc.Driver");
            this.con = DriverManager.getConnection(url, usuario, senha);
            JOptionPane.showMessageDialog(null,"Conexão efetuada com sucesso");
            return true;
        }catch(Exception e){
            JOptionPane.showMessageDialog(null,"Houve um erro durante a conexão com o banco de dados.\nErro: "+e.getMessage(),"erro de conexão", JOptionPane.ERROR_MESSAGE);
            return false;
        }
       }
public static void main(String[] args) {
        try{
         TestaConexao t = new TestaConexao();
         t.conectar();
        }catch(Exception err){
            System.out.println("Erro: "+err);
        }
    }
}

Com a conexão estabelecida já é possível interagir com o banco de dados de várias formas.


2. Comandos SQL: Structured Query Language
• Linguagem de consulta para acesso à banco de dados
• Subconjunto “padrão” implementado por todos os banco de dados
– SELECT
– FROM
– WHERE
– GROUP BY
– ORDER BY
– INNER JOIN
– INSERT
– UPDATE
– DELETE

2.1. SELECT: consulta, obter informações de uma tabela.
– Quais as tabelas e os campos que deverão ser selecionados.
– Ex: Selecionar todos os dados da tabela Tabela1.

SELECT * FROM Tabela1

Na consulta precedente, o asterisco (*) indica que todas as linhas e colunas da tabela Tabela1 devem ser selecionadas e Tabela1 especifica a tabela no banco de dados da qual os dados serão selecionados.

– Ex: Selecionar os campos Campo1 e Campo2 da tabela Tabela1

SELECT Campo1, Campo2 FROM Tabela1

WHERE
WHERE: fornece um critério de seleção no comando SELECT

SQL utiliza a cláusula WHERE opcional em uma consulta SELECT para especificar as opções de seleção para a consulta.

– Selecionar: especificando o campo
SELECT * FROM Tabela1 WHERE Id= 1
– Selecionar: especificando um “range”
SELECT * FROM Tabela1 WHERE Id > 1
SELECT * FROM Tabela1 WHERE Id > 1 and Id < 3
– Selecionar utilizando máscaras
SELECT * FROM Tabela1 WHERE Nome LIKE ‘J%’

A condição da cláusula WHERE pode conter os operadores <, >, <=, >=, =, <> e LIKE. O operador LIKE é utilizado para coincidência de padrão (pattern matching) com os caracteres curinga asterisco (*) e ponto de interrogação (?). A coincidência de padrão permite que o SQL procure strings semelhantes. Um asterisco (*) no padrão indica qualquer número de caracteres em seqüência na posição do asterisco dentro do padrão.

Exemplo:
Consulta localiza os registros de todos os Campo1 cujos valores começam com a letra d:

SELECT * FROM Tabela1 WHERE Campo1 LIKE 'd*'

Repare que o string de padrão é envolvido entre caracteres de aspas simples. Um ponto de interrogação (?) no string de padrão indica qualquer um caractere nessa posição no padrão. 

SQL diferencia letras maiúsculas de minúsculas em alguns sistemas de bancos de dados.
Nem todos os sistemas de banco de dados suportam o operador LIKE.
Por convenção, as palavras-chave de SQL devem utilizar todas as letras maiúsculas em sistemas que não fazem distinção entre letras maiúsculas e minúsculas para fazer as palavras-chave de SQL se destacarem em uma consulta de SQL.

ORDER BY
– ASC
– DESC
SELECT * FROM Clientes ORDER BY Nome ASC
SELECT * FROM Clientes ORDER BY Nome DESC


2.2. INSERT

2.2.1. Inserção parcial:

INSERT INTO Tabela1 (Campo1, Campo2, Campo3) VALUES (‘ValorCampo1’, ‘ValorCampo2’, ‘ValorCampo3’)

  Observações importantes:
a) Geralmente uma tabela possui o primeiro campo como um identificador, utiliza-se a abreviação id, este campo id é configurado na tabela para ser incrementado automaticamente (auto-increment), não necessita inserí-lo, ele também é configurado como uma chave-primária, ou seja, utilizado para identificar o registro (linha) da tabela, assim, Campo1 é a segunda coluna da tabela, pois a primeira coluna corresponde geralmente ao campo Id.

b) Campo1 corresponde ao nome da coluna da tabela e ValorCampo1 corresponde ao valor que será inserido na coluna Campo1 da tabela.

3) A seqüência (Campo1,...) VALUES ('ValorCampo1'...) deve corresponder a respectiva tabela, pode-se omitir algum campo na inserção, geralmente o CampoId é omitido por ser auto-increment.

Exemplo de tabela com duas inserções:

id
Campo1
Campo2
1
ValorCampo1
ValorCampo2
2
ValoCampo1
ValorCampo2

Com dados reais, tabela contatos:

id
nome
email
endereco
dataNascimento
1
José da Silva
js@sol.com.br
R. 7 de Setembro, 25
22/12/70
2
Maria do Carmo
mc@sol.com.br
Av. do Maias, 202
03/01/82

4) Uso de aspa simple e duplo
A instrução SQL e seus parâmetros dentro do código java são incluídos no código fonte entre aspas duplo " ".

String sql = "insert into contatos (nome, email, endereco, dataNascimento) values ('" + nome + "', '" + email + "', '" + endereco + "', '"+ dataNascimento +"')";

No browser esta instrução provavelmente não caberá na largura da janela e pulará para a próxima linha, porém esta instrução tem apenas uma linha.

O mesmo exemplo anterior utilizando o sinal de mais (+) para concaternas a instrução em quatro linhas

String sql = "insert into contatos " +
        "(nome,email,endereco, dataNascimento)" +
        " values ('" + nome + "', '" + email + "', '" +
        endereco + "', '"+ dataNascimento +"')";

Obs: Geralmente a divisão de instrução do código fonte em mais de uma linha é apenas para efeito didático, tornando possível a demostração do código em apenas uma tela, sem a necessidade de rolar a mesma, facilita a cópia pelos instruendos.

2.2.2. Inserção Total

INSERT INTO Tabela1 VALUES (‘ValorCampo1’, ‘ValorCampo2’, ‘ValorCampo3’)

Desta forma todos os campos da tabela deverão ser incluídos no comando INSERT.


2.2.3. Inserção de valor indefinido

INSERT INTO Tabela1 (Campo1, Campo2, Campo3) VALUES (?, ?, ?)

Utiliza-se o ponto de interrogação (?) como parâmetro nos lugares dos valores que ainda não estão definidos, ou seja, o comando INSERT é executado e os valores dos parâmetro serão definidos posteriormente com a instrução statement.

Caracteres de ponto de interrogação são referênciados de acordo com sua posição, com o primeiro ponto de interrogação sendo referenciado pelo número 1, o segundo pelo número 2 e assim sucessivamente.
O processo de fornecimento de variáveis Java para um prepared statemanet é conhecido como variáveis de bind. Para fornecer variáveis para o prepared statement SQL, deve-se usar o método set (escrita). Esses métodos são semelhantes aos métodos get (leitura).

As cláusulas são executadas em um banco de dados através da interface PreparedStatement. Para receber um PreparedStatement relativo à conexão, basta chamar o método prepareStatement, passando como argumento o comando SQL com os valores vindos de variáveis preenchidos com uma interrogação.

String sql = "insert into contatos " +
                "(nome,email,endereco,dataNascimento) " +
                "values (?,?,?,?)";
PreparedStatement stmt = connection.prepareStatement(sql);

Logo em seguida, chamamos o método setString do PreparedStatement para preencher os valores que são do tipo String, passando a posição (começando em 1) da interrogação no SQL e o valor que deve ser colocado:

// preenche os valores
stmt.setString(1, "José Pedro");
stmt.setString(2, "1234 542");
stmt.setString(3, "jpedro@bol.com.br");

Para definir a data de nascimento, utiliza-se um objeto do tipo java.sql.Date para passar o PreparedStatement. Para passar a data atual usa-se um long que representa os milissegundos da data atual dentro de um java.sql.Date que é o tipo suportado pela API JDBC.  Com a classe Calendar se consegue a data em milissegundos:

java.sql.Date dataParaGravar = new java.sql.Date(Calendar.getInstance().getTimeInMillis());
stmt.setDate(4, dataParaGravar);

Por fim, uma chamada a execute(), executa o comando SQL:

stmt.execute();


Veja o exemplo abaixo, que abre uma conexão e insere um os dados no banco:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class DAO {

    public Connection conectar() {
        try {
            String user = "root";
            String passwd = "";
            String url = "jdbc:mysql://127.0.0.1:3306/programador";
            Class.forName("com.mysql.jdbc.Driver");
            Connection conexao = DriverManager.getConnection(url, user, passwd);
            return conexao;
        } catch (Exception ex) {
            Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }

    }
 
        Connection conn = conectar();
        String sql = "insert into aluno (nome, endereco, matricula )  values (?, ?, ?)";

        PreparedStatement prep = conn.prepareStatement(sql);
        prep.setString(1, "José Maria);
        prep.setString(2, "Rua dos Maias, 202);
        prep.setLong(3, "212");
        prep.execute();
        prep.close();
        conn.close();
    }
}


2.3. DELETE

DELETE FROM Tabela1 WHERE Id=1

DELETE FROM Tabela1 WHERE Campo1=‘ValorCampo1’ and Campo2=‘ValorCampo2’
- Pode ser definido um conjunto de valores

2.4. UPDATE

UPDATE Tabela1SET Campo1=‘ValorCampo1’ WHERE Id= 1

Pode ser muito útil definir o número de linhas afetadas por uma instrução de atualização. O objeto Statement fornece um método, getUpDateCount () para fazer exatamente  isso. Retorna um valor inteiro depois de qualquer tipo de operação de atualização que possa ser utilizado para determinar se algumas linhas foram afetadas pela operação ou não, e quantas  linhas foram afetadas.

2.5. JOIN
SELECT Tabela1.Tabela1ID, Campo1, Campo2, Campo3 FROM Tabela1

INNER JOIN Tabela2 ON Tabela1.Tabela1ID= Tabela2Tabela2ID ORDER BY Nome ASC

As vinculações entre tabelas se realizam mediante a cláusula INNER que combina registros de duas tabelas sempre que tiver concordância de valores em um campo comum. Sua sintaxe é:

SELECT campos FROM tb1 INNER JOIN tb2 ON tb1.campo1 comparador tb2.campo2

Onde:

tb1, tb2São os nomes das tabelas desde as que se combinam os registros.
campo1, campo2São os nomes dos campos que se combinam. Se não são numéricos, os campos devem ser do mesmo tipo de dados e conter o mesmo tipo de dados, porém não tem que ter o mesmo nome.
comparadoÉ qualquer operador de comparação relacional: =, <,<>, <=, =>, ó >.

Pode-se utilizar uma operação INNER JOIN em qualquer cláusula FROM. Isto cria uma combinação por equivalência, conhecida também como união interna. As combinações equivalentes são as mais comuns; estas combinam os registros de duas tabelas sempre que tiver concordância de valores em um campo comum a ambas tabelas. Pode-se utilizar INNER JOIN com as tabelas Departamentos e Empregados para selecionar todos os empregados de cada departamento. Do contrário, para selecionar todos os departamentos (inclusive se algum deles não tiver nenhum empregado atribuído) se emprega LEFT JOIN ou todos os empregados (inclusive se algum não estiver atribuído a nenhum departamento), neste caso RIGHT JOIN.

Se se tenta combinar campos que contenham dados Memo ou Objeto OLE, se produz um erro. Podem-se combinar dois campos numéricos quaisquer, inclusive se são de diferentes tipos de dados. Por exemplo, pode combinar um campo Numérico para o que a propriedade Size de seu objeto Field está estabelecida como Inteiro, e um campo Contador.

O exemplo seguinte mostra como poderia combinar as tabelas Categorias e Produtos baseando-se no campo IDCategoria:

SELECT
    NomeCategoria, NomeProduto
FROM
   Categorias
INNER JOIN
   Produtos
ON
    Categorias.IDCategoria = Produtos.IDCategoria

No exemplo anterior, IDCategoria é o campo combinado, porém não está incluído na saída da consulta já que não está incluído na instrução SELECT. Para incluir o campo combinado, incluir o nome do campo na instrução SELECT, neste caso, Categorias.IDCategoria.

Também se pode linkar várias cláusulas ON em uma instrução JOIN, utilizando a seguinte sintaxe:

SELECT campos FROM tabela1 INNER JOIN tabela2
ON (tb1.campo1 comp tb2.campo1 AND ON tb1.campo2 comp tb2.campo2)
OR ON (tb1.campo3 comp tb2.campo3)

Também pode aninhar instruções JOIN utilizando a seguinte sintaxe:

SELECT campos FROM tb1 INNER JOIN (tb2 INNER JOIN [( ]tb3
[INNER JOIN [( ]tablax [INNER JOIN ...)]
ON tb3.campo3 comp tbx.campox)]
ON tb2.campo2 comp tb3.campo3)
ON tb1.campo1 comp tb2.campo2

Um LEFT JOIN ou um RIGHT JOIN pode se aninhar dentro de um INNER JOIN, porém um INNER JOIN não pode se aninhar dentro de um LEFT JOIN ou um RIGHT JOIN.

Exemplo:

SELECT DISTINCT
   Sum(PrecoUnitario * Quantidade) AS Sales,
(Nome + ' ' + Sobrenome) AS Name
FROM
    Empregados
INNER JOIN(
    Pedidos
INNER JOIN
   DetalhesPedidos
   ON
   Pedidos.IdPedido = DetalhesPedidos.IdPedido)
    ON
    Empregados.IdEmpregado = Pedidos.IdEmpregado
GROUP BY
   Nome + ' ' + Sobrenome

(Cria duas combinações equivalentes: uma entre as tabelas Detalhes de pedidos e Pedidos, e a outra entre as tabelas Pedidos e Empregados. Isto é necessário já que a tabela Empregados não contem dados de vendas e a tabela Detalhes de pedidos não contem dados dos empregados. A consulta produz uma lista de empregados e suas vendas totais.)

Se empregarmos a cláusula INNER na consulta se selecionarão só aqueles registros da tabela da que tivermos escrito à esquerda de INNER JOIN que contenham ao menos um registro da tabela que tivermos escrito à direita. Para solucionar isto temos duas cláusulas que substituem a palavra-chave INNER, estas cláusulas são LEFT e RIGHT. LEFT toma todos os registros da tabela da esquerda embora não tenha nenhum registro na tabela da esquerda. RIGHT realiza a mesma operação, porém ao contrário, toma todos os registros da tabela da direita embora não tenha nenhum registro na tabela da esquerda.

A sintaxe exposta anteriormente pertence a ACCESS, onde todas as sentenças com a sintaxe funcionam corretamente. Os manuais de SQL-SERVER dizem que esta sintaxe é incorreta e que há que adicionar a palavra reservada OUTER: LEFT OUTER JOIN e RIGHT OUTER JOIN. Na prática funciona corretamente de uma forma ou de outra.

Não obstante, os INNER JOIN ORACLE não é capaz de interpretá-los, mas existe uma sintaxe em formato ANSI para os INNER JOIN que funcionam em todos os sistemas. Tomando como referência a seguinte sentença:

SELECT
   Faturas.*,
   Alvaras.*
FROM
   Faturas
INNER JOIN
   Alvaras
ON
   Faturas.IdAlvara = Alvaras.IdAlvara
WHERE
   Faturas.IdCliente = 325

A transformação desta sentença a formato ANSI seria a seguinte:

SELECT
Faturas.*,
Alvaras.*
FROM
Faturas, Alvaras
WHERE
Faturas.IdAlvara = Alvaras.IdAlvara
AND
Faturas.IdCliente = 325

Como se pode observar as mudanças realizadas foram as seguintes:

Todas as tabelas que intervêm na consulta se especificam na cláusula FROM.
As condições que vinculam às tabelas se especificam na cláusula WHERE e se vinculam mediante o operador lógico AND.

Referente aos OUTER JOIN, não funcionam em ORACLE e ademais conheço uma sintaxe que funciona nos três sistemas. A sintaxe em ORACLE é igual à sentença anterior, porém adicionando os caracteres (+) atrás do nome da tabela na qual desejamos aceitar valores nulos, isto equivale a um LEFT JOIN:

SELECT
   Faturas.*,
    Alvaras.*
FROM
   Faturas, Alvaras
WHERE
   Faturas.IdAlvara = Alvaras.IdAlvara (+)
   AND
   Faturas.IdCliente = 325

E isto a um RIGHT JOIN:

SELECT
   Faturas.*,
    Alvaras.*
FROM
   Faturas, Alvaras
WHERE
   Faturas.IdAlvara (+) = Alvaras.IdAlvara
   AND
   Faturas.IdCliente = 325

Em SQL-SERVER se pode utilizar uma sintaxe parecida, neste caso não se utiliza os caracteres (+), e sim os caracteres =* para o LEFT JOIN e *= para o RIGHT JOIN.

Consultas de Auto-combinação

A auto-combinação se utiliza para unir uma tabela consigo mesma, comparando valores de duas colunas com o mesmo tipo de dados. A sintaxe na seguinte:

SELECT
   alias1.coluna, alias2.coluna, ...
FROM
   tabla1 as alias1, tabela2 as alias2
WHERE
   alias1.coluna = alias2.coluna
AND
   outras condicoes

Por exemplo, para visualizar o número, nome e posto de cada empregado, junto com o número, nome e posto do supervisor de cada um deles se utilizaria a seguinte sentença:

SELECT
   t.num_emp, t.nome, t.posto, t.num_sup,s.nome, s.posto
FROM
    empregados AS t, empregados AS s
WHERE
   t.num_sup = s.num_emp

Consultas de Combinações não Comuns

A maioria das combinações está baseada na igualdade de valores das colunas que são o critério da combinação. As não comuns se baseiam em outros operadores de combinação, tais como NOT, BETWEEN, <>, etc.

Por exemplo, para listar o grau salarial, nome, salário e posto de cada empregado ordenando o resultado por grau e salário haveria que executar a seguinte sentença:

SELECT
   graus.grau,empregados.nome, empregados.salario, empregados.posto
FROM
    empregados, graus
WHERE
   empregados.salario BETWEEN grados.salarioinferior And grados.salariosuperior
ORDER BY
   grados.grado, empregados.salario

Para listar o salário médio dentro de cada grau salarial haveria que lançar esta outra sentença:

SELECT
   graus.grau, AVG(empregados.salario)
FROM
   empregados, graus
WHERE
   empregados.salario BETWEEN graus.salarioinferior And graus.salariosuperior
GROUP BY
   graus.grau

CROSS JOIN (SQL-SERVER)

Utiliza-se em SQL-SERVER para realizar consultas de união. Suponhamos que temos uma tabela com todos os autores e outra com todos os livros. Se desejássemos obter uma listagem combinando ambas tabelas de tal forma que cada autor aparecesse junto a cada título, utilizaríamos a seguinte sintaxe:

SELECT
    Autores.Nome, Livros.Titulo
FROM
    Autores CROSS JOIN Livros

SELF JOIN

SELF JOIN é uma técnica empregada para conseguir o produto cartesiano de uma tabela consigo mesma. Sua utilização não é muito freqüente, mas colocaremos algum exemplo de sua utilização.
Suponhamos a seguinte tabela (O campo autor é numérico, embora para ilustrar o exemplo utilize o nome):

Autores
Código (Código do livro)Autor (Nome do Autor)
B00121. Francisco López
B00122. Javier Alonso
B00123. Marta Rebolledo
C00141. Francisco López
C00142. Javier Alonso
D01202. Javier Alonso
D01203. Marta Rebolledo

Queremos obter, para cada livro, pares de autores:

SELECT
    A.Codigo, A.Autor, B.Autor
FROM
   Autores A, Autores B
WHERE
   A.Codigo = B.Codigo

O resultado é o seguinte:


CódigoAutorAutor
B00121. Francisco López1. Francisco López
B00121. Francisco López2. Javier Alonso
B00121. Francisco López3. Marta Rebolledo
B00122. Javier Alonso2. Javier Alonso
B00122. Javier Alonso1. Francisco López
B00122. Javier Alonso3. Marta Rebolledo
B00123. Marta Rebolledo3. Marta Rebolledo
B00123. Marta Rebolledo2. Javier Alonso
B00123. Marta Rebolledo1. Francisco López
C00141. Francisco López1. Francisco López
C00141. Francisco López2. Javier Alonso
C00142. Javier Alonso2. Javier Alonso
C00142. Javier Alonso1. Francisco López
D01202. Javier Alonso2. Javier Alonso
D01202. Javier Alonso3. Marta Rebolledo
D01203. Marta Rebolledo3. Marta Rebolledo
D01203. Marta Rebolledo2. Javier Alonso

Como podemos observar, os pares de autores se repetem em cada um dos livros, podemos omitir estas repetições da seguinte forma:

SELECT
   A.Codigo, A.Autor, B.Autor
FROM
   Autores A, Autores B
WHERE
   A.Codigo = B.Codigo AND A.Autor < B.Autor

O resultado agora é o seguinte:


CódigoAutorAutor
B00121. Francisco López2. Javier Alonso
B00121. Francisco López3. Marta Rebolledo
C00141. Francisco López2. Javier Alonso
D01202. Javier Alonso3. Marta Rebolledo

Agora temos um conjunto de resultados em formato Autor - CoAutor.
Se na tabela de empregados quiséssemos extrair todos os possíveis pares que podemos realizar, utilizaríamos a seguinte sentença:

SELECT
    Homens.Nome, Mulheres.Nome
FROM
   Empregados Homem, Empregados Mulheres
WHERE
   Homem.Sexo = 'Homem' AND
   Mulheres.Sexo = 'Mulher' AND
    Homens.Id <>Mulheres.Id

Para concluir suponhamos a seguinte tabela:

Empregados
IdNomeSeuChefe
1Marcos6
2Lucas1
3Ana2
4Eva1
5Juan6
6Antonio

Queremos obter um conjunto de resultados com o nome do empregado e o nome de seu chefe:

SELECT
    Empre.Nome, Chefes.Nome
FROM
   Empregados Empre, Empregados Chefe
WHERE
   Empre.SeuChefe = Chefes.Id


Outros comandos SQL

- CREATE TABLE — cria uma tabela com as colunas e seus respectivos tipos de dados fornecidos pelo usuário. Os tipos de dados, variam conforme o SGBD, portanto é necessário utilizar os metadados para estabelecer quais os tipos de dados suportados pelo SGBD.
- DROP TABLE — apaga todas as linhas de uma tabela e remove a definição do banco de dados.
- ALTER TABLE — adiciona ou remove colunas de uma tabela.

O quadro a seguir mostra algumas das principais palavras-chave de SQL.

Palavra-chave Descrição
SELECT Recupera dados de uma ou mais tabelas.
FROM Tabelas envolvidas na consulta. Requeridas em cada SELECT.
WHERE Critérios de seleção que determinam as linhas a ser recuperadas, excluídas ou
atualizadas. Opcional em uma consulta ou uma instrução SQL.
GROUP BY Critérios para agrupar linhas. Opcional em uma consulta SELECT.
ORDER BY Critérios para ordenar linhas. Opcional em uma consulta SELECT
INNER JOIN Mescla linhas de múltiplas tabelas.
INSERT Insere linhas em uma tabela específica.
UPDATE Atualiza linhas em uma tabela específica.
DELETE Exclui linhas de uma determinada tabela

3. Criar statements

Cria-se um objeto da classe Statement para armazenar o pedido que será enviado ao SGBD. Informa-se a cadeia de strings que representa o comando SQL que será utilizado e depois executa-o, usando métodos especiais dependendo do tipo de comando desejado.

Para realizar uma operação de consulta é necessário criar uma instância da classe Statement para depois executar o método executeQuery(String sql).

String SQL = “select * from tabela1”;
Statement stmt= con.createStatemant();

3.1. Prepared statements permitem o desenvolvimento de templates de queries SQL que podem ser reutilizadas para efetuar queries iguais com diferentes valores de parâmetros. Prepared statements são criados a partir de um objeto Connection, ou seja, são iguais a objetos Statement.

Cria-se uma query, que pode ser de qualquer tipo, deixando os valores das variáveis indefinidos. Então especifica-se os valores para os elementos indefinidos antes de executar a query e repetir de acordo com a necessidade do código.

Prepared statements são criados a partir de um objeto Connection, ou seja, são iguais a objetos Statement. Na hora de construir a query, substitua os valores das variáveis com o simbolo de interrogação(?).

PreparedStatement, basicamente prepara o sql sobre uma string realizando substituições e validações, antes de ser executado no Banco de dados.


Diferença entre Statement e PreparedStatement

O motivo mais convincente para escolher entre um e outro é: se a instrução SQL (seja insert, delete, update ou select) é fixa (não muda nunca), usa-se o Statement. Mas se for usado comandos dinâmicos, que se alteram em tempo de execução mediante a passagem de parâmetros, então o ideal é usar PreparedStatement. É possível usar Statement para montar comandos dinâmicos sim, mas tem  que ficar concatenando strings, delimitando valores do tipo caractere com aspa simples... com PreparedStatement fica bem mais fácil e o código fica mais limpo.

String sql = "INSERT INTO Tabela1 VALUES(?, ?, ?)";
PreparedStatement cstmt = con.prepareStatement(sql);

cstmt.setInt(1, 18943);
cstmt.setString(2, "Lima Barreto");
cstmt.setString(3, "O Homem que Sabia Javanês");
cstmt.executeUpdate();
...

Os comandos SQL são passados pelo método prepareStatement para o SGBD que compila e armazena na memória.
O método prepareStatement retorna um objeto de uma classe que implementa a interface PreparedStatement.


No servidor é criado um cursor (implícito) quando se executa um comando SQL. Esse cursor contém um número máximo de linhas que sendo criado sob demanda, ou seja, à medida que a máquina cliente faz as solicitações, o SGBD manda mais linhas de registros (ex: 40 em 40 linhas) para o cursor.

Quando é solicitado no rs, são trazidos as linhas da tabela para o micro do cliente (memória). A cada execução de rs.next() o micro cliente pede mais as linhas da tabela.


4. Consulta

String SQL = “select * from tabela1”;
Statement stmt = con.createStatement();
stmt.executeQuery(SQL);

5. Obtendo retorno de dados via ResultSet
Quando se executa um comando SELECT, se obtém  os registros de uma ou mais tabelas do banco de dados. O método executeQuery retorna dados. Assim, deve-se ter outro tipo de classe que serve exclusivamente para recebe esses dados, de maneira que se possa fazer uso deles no processamento. Essa classe é chamada de ResultSet. É necessário instanciar um objeto desta classe que receba o resultado do executeQuery.

java.sql.ResultSet rs = stmt.executeQuery("SELECT CAMPO1, CAMPO2, CAMPO3 FROM  TABELA1");
// enquanto houver registros no ResultSet ...
while(rs.next()){
   String campo1 = rs.getString("CAMPO1");
   String campo2 = rs.getString("CAMPO2");
   String campo3 = rs.getString("CAMPO3");
   java.sql.Date campo4 = rs.getDate("CAMPO4");
...
stmt.close();
connection.close();

A partir de agora, tem se um objeto chamado rs que armazena todos os dados recebidos. Em linhas gerais, é necessário estabelecer um loop onde fazem a leitura dos dados baseados em métodos presentes na classe ResultSet. Esses métodos permitem "andar" pelos resultados e copiar os valores lidos para outros objetos.

5.1. java.sql.ResultSet (1)

Interface que provê acesso aos dados. Resultset é uma lista encadeada, portanto, só tem como referencia o primeiro e o ultimo elementos. O próximo elemento só pode ser acessado a partir do elemento anterior. Não tem como fazer uma referência direta.

O objeto ResultSet é gerado a partir do método Statement.executeQuery(String sql)
O objeto ResultSet, quando criado, mantém um cursor posicionado antes da primeira linha de dados

5.2. java.sql.ResultSet (2)

Para movimentar o cursor uma posição na tabela de dados, utiliza-se o comando next()
next() - move o cursor um linha abaixo retornando true quando existe linha para o cursor ser posicionado

5.3. java.sql.ResultSet (3)

Para realizar leitura do conteúdo das colunas utiliza-se os métodos:
getXXXX(String nome_coluna)
getXXXX(int indice_coluna)
onde XXXX é o tipo de dados esperado

ex.: para retornar o conteúdo de uma coluna que armazena o tipo Float usa-se o método getFloat ou getDouble, se o tipo for VarChar usa-se o método getString

Tabela de métodos get do ResultSet (recuperar dados) e métodos set do PreparedStatement (preenche o comando SQL).


Tipo do SQL
Método do
ResulSet
Método do PreparedStatement
Tipo Java
TINYINT
 getByte setByte byte
SMALLINT getShort setShort
short
INTEGER
getInt setInt
int
BIGINT
getLong setLong
long
REAL
getFloat setFloat
float
FLOAT getFloat setFloat
float
DOUBLE
getDouble setDouble
double
VARCHAR ou LONGVARCHAR getString setString
java.lang.String
 DECIMAL, NUMERIC getBigDecimal setBigDecimal java.math.BigDecimal
 CHAR getString setString
String
VARCHAR, LONGVARCHAR getString setString
java.lang.String
BINARY ou VARBINARY getBytes setBytes byte[]
DATE getDate setDate java.sql.Date
TIME getTime setTime
java.lang.Time
TIMESTAMP getTimestamp setTimestamp
java.sql.TimeStamp
LONGVARCHAR getASCIIStream setASCIIStream java.io.InputStream
LONGVARCHAR getUnicodeStream
java.io.InputStream
LONGVARBINARY getBinaryStream setBinaryStream java.io.InputStream
BIT
getBoolean
setBoolean boolean
Todos os tipos getObject
Object
NULL

setNull


6. Tratamento de Erros

Quase todos os métodos da API JDBC podem lançar uma exceção do tipo SQLException, que representa algum tipo de erro ocorrido no acesso ao banco, seja falha na conexão, SQL mal formado, até violação de PK etc.
A classe SQLException possui os métodos:
- getMessage() - retorna a mensagem de erro.
- getSQLState() - retorna um dos códigos de estado do padrão ANSI-92 SQL.
- getErrorCode() - retorna o código de erro específico do fornecedor.
- getNextException() - retorna a exceção aninhada (encadeada), se houver.


7. Resumo

7.1. Registro do Driver           

Class.forName("com.mysql.jdbc.Driver");
7.2. Conexão com o banco de dados

Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bancodados", "root", "2345");
7.3. Seleção

String SQL = “select * from tabela1”;
4. Armazenamento

String SQL = “select * from tabela1”;
Statement stmt= con.createStatemant();
7.5. Consulta

String SQL = “select * from tabela1”;
Statement stmt = con.createStatement();
stmt.executeQuery(SQL);
7.6. Obtendo o retorno dos dados via ResultSet

java.sql.ResultSet rs = stmt.executeQuery("SELECT CAMPO1, CAMPO2, CAMPO3 FROM  TABELA1");
// enquanto houver registros no ResultSet ...
while(rs.next()){
   String campo1 = rs.getString("CAMPO1");
   String campo2 = rs.getString("CAMPO2");
   String campo3 = rs.getString("CAMPO3");
   java.sql.Date campo4 = rs.getDate("CAMPO4");
...
stmt.close();
connection.close();

8. Passando os dados do ResulSet para uma ArryList (coleção) para que fique disponínel para manipulação:

Exemplo:

List pessoas = new ArrayList(); 

while( rs.next ) 

Pessoa p = new Pessoa(); 

    p.setNome( rs.getString( "nome" ) );     
    p.setCpf( rs.getString( "cpf" ) );  
  
    p.setFoto( rs.getBytes( "foto" ) );  
   
    pessoas.add( p ); 

}