API JDBC - LSI/USP

Propaganda
PROGRAMAÇÃO
DISTRIBUÍDA EM JAVA
Verão/2001
Aula 10
API JDBC
09.02.2001
Luciano Silva
e-mail: [email protected]
1
2
Tópicos da Aula 10
 API JDBC
 Arquitetura das camadas JDBC
 Classes principais de java.sql
 Abertura e fechamento de conexões
 Consulta simples
 Métodos para acessos a dados de registros
 Consultas parametrizadas
 Stored Procedures
 Transações
3
API JDBC (I)
A API JDBC (Java Database Connectivity)
disponibiliza para aplicações Java uma
interface para acessar bancos de dados
baseados em SQL.
Do ponto de vista das aplicações, o
programador somente precisa se preocupar
com detalhes de alto nível com o servidor
de banco de dados.
4
API JDBC (II)
A API JDBC funciona num nível denominado
“call level” ( nível de chamada ). Isto
significa que as aplicações invocam
consultas SQL, estas são passadas para o
servidor de BD, executadas e resultados são
devolvidos para a aplicação.
5
API JDBC (III)
O único trabalho da aplicação é:
1. Abrir conexão com o servidor de BD
2. Construir uma consulta SQL ou chamada
para stored procedure usando classes da API
3. Solicitar a execução
4. Receber e utilizar os dados
5. Fechar a conexão
6
API JDBC (IV)
Todos os detalhes de comunicação de baixo
nível com o servidor de BD é feito através
de classes especiais chamadas “driver
classes”, ou simplesmente, drivers.
Existe uma grande variedade de drivers já
disponibilizados para JDBC. Existe, ainda,
a possibilidade de se construir novos drivers
a partir de classes e interfaces da API.
7
Por que usar JDBC ?
1. Facilidade de manutenção
2.Conectividade com ampla
servidores de BD
3. Portabilidade
4. Prototipação rápida
gama
de
8
Cenários de uso de JDBC
Existe uma série de sistemas, principalmente
aqueles implementados em Java e em
C/C++, onde o uso de JBDC está se
tornando constante. Com Java, constrói-se
interfaces portáveis para os clientes e são
incorporados nestas interfaces mecanismos
para acesso bastante simplificado a banco
de dados.
9
Cenário (I) - Applets
10
Cenário (II) - Aplicações Desktop
11
Cenário (III) - Aplicações Multi-tier
12
Arquitetura JDBC
13
Estrutura da API JDBC
As classes da API JDBC utilizadas pelas
aplicações estão concentradas em dois
pacotes:
 java.sql.*
 javax.sql.*
14
Algumas classes básicas do pacote
java.sql
Existem quatro classes abstratas básicas muito
utilizadas:
 DriverManager
 Connection
 Statement
 ResultSet
15
Relação entre as classes
16
Classe DriverManager
Esta classe é responsável por:
 manipular o carregamento de drivers
 providenciar suporte para criação de novas
conexões
17
Classe Connection
Representa uma conexão com um banco de
dados particular.
A partir de Connection pode-se:
 criar consultas
 criar consultas parametrizadas
 criar chamadas para stored procedures
 encerrar uma conexão
18
Classe Statement
Age como um container para executar uma
consulta SQL para uma determinada
conexão.
Para consultas mais complicadas ( consultas
parametrizadas) ou invocação de stored
procedures, são utilizadas classes mais
específicas
que
Statement
(
PreparedStatement e CallableStatement).
19
Classe ResultSet
Controla o acesso aos registros resultantes de
uma
consulta
via
Statement,
PreparedStatement ou CallableStatement.
Possui métodos para navegar nos registros de
resultados, como também consultar dados
destes registros.
20
Outras classes
pacotes
e
interfaces
do
No pacote java.sql.* existe uma série de
outras classes, interfaces e subpacotes que
permitem, por exemplo, construir classes do
tipo driver para acesso específico.
21
Abrindo conexões (I)
Existe uma série de detalhes envolvidos na
abertura de conexão com um servidor de
BD que devem ser providenciados pela
aplicação.
Para estas providências, geralmente são
utilizadas
as
classes
Drivermanager(java.sql) e Class(java.lang).
22
Abrindo conexões (II)
Detalhes que precisam ser providenciados:
1. Especificar URL de acesso com protocolos
de banco de dados adequados
2. Especificar parâmetros de conexão, tais
como nome do usuário e senha
3. Registrar drivers para uso da aplicação
23
URL de acesso ao servidor de BD
A sintaxe mais utilizada para acessar um
servidor de banco de dados é a seguinte:
jdbc:<subprotocolo>:<subnome>
onde subprotocolo significa o protocolo do
driver e subnome geralmente indica
servidor, porta e banco de dados.
24
Exemplos de URL de acesso
jdbc:odbc:fred
– subprotocolo: odbc
– fonte de dados: fred
jdbc:dbnet://wombat:356/fred
– subprotocolo:dbnet
– servidor : wombat
– porta 356
– fonte de dados: fred
25
Argumentos para URL de acesso
Em algumas situações, parâmetros de conexão
tais como usuário e senha são necessários.
Neste caso, utiliza-se os nomes dos
parâmetros
juntamente
com
seus
respectivos valores.
Cada par parâmetro-valor é separado por um
ponto-e-vírgula (;);
26
Exemplos
jdbc:odbc:wombat;CacheSize=20
– Parâmetro de tamanho de cache
jdbc:odbc:qeora;UID=kgh;PWD=foo
ey
– parâmetro UID ( Identificação )
– parâmetro PWD ( Senha
)
27
Registro de drivers
A camada de gerenciamento do JDBC precisa
saber quais os drivers de banco de dados
estão disponibilizados.
A maneira mais simples de executar tal tarefa
é carregar explicitamente os drivers a serem
utilizados, valendo-se do método forName
da classe Class ( pacote java.lang ).
28
Exemplo
Para carregar, por exemplo, o driver chamado
acme.db.Driver, utiliza-se:
Class.forName("acme.db.Driver");
Para carregar, o driver da ponte JDBC-ODBC
utiliza-se:
Class.forName(”jdbc.odbc.JdbcOdbcDriver");
29
Considerações
drivers
sobre
carregamento
de
 Os drivers são carregados antes de serem
estabelecidas as conexões
 A priori, podem ser carregados quantos
drivers forem necessários para a aplicação.
30
Considerações
drivers
sobre
carregamento
de
 Os drivers são carregados antes de serem
estabelecidas as conexões
 A priori, podem ser carregados quantos
drivers forem necessários para a aplicação.
31
Exemplo completo - Abertura/fechamento
da conexão
import java.net.URL;
import java.sql.*;
class exemplo {
public static void main(String argv[]) {
try {
String url = "jdbc:odbc:wombat";
Class.forName(“jdbc.odbc.JdbcOdbcDriver”);
Connection con = DriverManager.getConnection(url, "kgh", "");
con.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
32
Executando consultas simples...
Para se executar consultas simples são
utilizados os seguintes passos:
1. Cria-se um Statement com base na conexão
2. Chama-se o método executeQuery do
Statement com a consulta SQL, devolvendo
o resultado numa “instância” de ResultSet
33
Qual especificação
utilizar?
do
SQL
pode-se
A priori, espera-se que o servidor de BD
suporte, pelo menos, o padrão ANSI SQL
92, que é bastante abrangente para a maioria
das aplicações.
Se o servidor suporta construções específicas,
tais como consultas com SQL temporal, a
cláusula por ser enviada sem problemas
como parâmetro do método executeQuery.
34
Exemplo consulta
...
Connection con = DriverManager.getConnection(url, "kgh", "");
// Cria-se Statement com base na conexão con
Statement stmt = con.createStatement();
// Executa-se a consulta dos campos a,b,c da table1
ResultSet r = stmt.executeQuery("SELECT a, b, c FROM
Table1");
...
35
Como navegar no resultado ?
A partir do objeto ResultSet pode-se:
 verificar se existem mais registros para
serem lidos, com o método next()
 pode-se consultar os campos através de
métodos específicos, que mapeam o tipo
SQL para o tipo Java. Por exemplo,
getString(campo) obtém um campo do tipo
string.
36
Exemplo
...
Connection con = DriverManager.getConnection(url, "kgh", "");
// Cria-se Statement com base na conexão con
Statement stmt = con.createStatement();
// Executa-se a consulta dos campos a,b,c da table1
ResultSet r = stmt.executeQuery("SELECT a, b, c FROM
Table1");
while (r.next()) {
int i = r.getInt("a");
String s = r.getString("b");
byte b[] = r.getBytes("c");
System.out.println("ROW = " + i + " " + s + " " + b[0]);
}
...
37
Objeto ResultSet revisitado
A partir do objeto ResultSet pode-se:
 verificar se existem mais registros para serem
lidos, com o método next()
 pode-se consultar os campos através de métodos
específicos, que mapeam o tipo SQL para o tipo
Java. Por exemplo, getString(campo) obtém um
campo do tipo string.
38
Exemplo
...
Connection con = DriverManager.getConnection(url, "kgh", "");
// Cria-se Statement com base na conexão con
Statement stmt = con.createStatement();
// Executa-se a consulta dos campos a,b,c da table1
ResultSet r = stmt.executeQuery("SELECT a, b, c FROM
Table1");
while (r.next()) {
int i = r.getInt("a");
String s = r.getString("b");
byte b[] = r.getBytes("c");
System.out.println("ROW = " + i + " " + s + " " + b[0]);
}
...
39
Métodos para obtenção dos dados
O primeiro problema de se usar um objeto ResultSet
é descobrir qual método utilizar para obter
determinado campo do conjunto resultado da
consulta.
Para grande parte dos tipos de dados existem
algumas possibilidades múltiplas para consulta,
embora algumas sejam recomendadas.
40
Tabela de métodos por tipo (I)
x - funciona
X - recomendado
41
Tabela de métodos por tipo (II)
x - funciona
X - recomendado
42
Tabela de métodos por tipo (III)
x - funciona
X - recomendado
43
Obtenção de Large Objects (LOB)
Em algumas situações, os métodos anteriores não
são suficientes para obter grandes objetos numa
linha, tais como objetos BLOB ( Binary Large
Object) ou CLOB ( Character Large Object).
Objetos ResultSet disponibilizam métodos de leitura
de objetos de tal tipo, disponibilizando streams de
entrada.
44
Métodos para leitura de LOB
São disponibilizados três métodos:
 getBinaryStream():
para
leitura
objetos binários
 getAsciiStream():
para
leitura
caracteres ASCII
 getUnicodeStream(): para leitura
caracteres Unicode
de
de
de
45
Exemplo
Statement stmt = con.createStatement();
ResultSet r = stmt.executeQuery("SELECT x FROM Table2");
byte buff = new byte[4096];
while (r.next()) {
InputStream fin = r.getAsciiStream(1);
for (;;) {
int size = fin.read(buff);
if (size == -1) { // Se chegou no final do objeto
break;
}
... Processa parte do objeto lido em buff
}
}
46
Valores NULL (I)
Valores de registros com NULL podem aparecer
numa tabela de banco de dados.
NULL é aplicado principalmente em:
 quando o campo não se aplica ao registro
 quando o valor do campo para o registro é
desconhecido
47
Valores NULL (II)
Quando se invoca um método de obtenção do tipo
get*(campo) e o campo contém NULL, o
resultado depende do método invocado:
 null : retornado por métodos que devolvem
objetos Java (getString, getBigDecimal, getBytes,
getDate, getTime, getTimestamp, getAsciiStream,
getUnicodeStream , getBinaryStream , getObject)
48
Valores NULL (III)
 0 : retornado por métodos de devolvem valores
numéricos (getByte, getShort, getInt, getLong,
getFloat, and getDouble)
 false : retornado pelo método de consulta de
valores booleanos getBoolean.
49
Classe PreparedStatement (I)
A classe PreparedStatement é uma classe derivada
de Statement.
Prepared Statement representa uma cláusula SQL de
forma pré-compilada, cuja execução pode ser mais
rápida que cláusulas SQL construídas com
Statement.
Cláusulas usadas constantemente geralmente são
construídas com PreparedStatement ao invés de
Statement.
50
Classe PreparedStatement (II)
PreparedStatement é usada em duas situações:
 deixar a cláusula SQL pré-compilada
 permitir a definição de parâmetros da consulta em
tempo de execução ( consultas parametrizadas ).
Os parâmetros de uma consulta recebem o nome
técnico de parâmetros IN.
51
Classe PreparedStatement (III)
A utilização de PreparedStament é análoga à da
classe Statement:
1. Com base em uma conexão, cria-se uma
“instância” de PreparedStatement
2. Chama-se o método de execução adequado
3. Obtém-se os resultados em ResultSet, caso
necessário
52
Consultas parametrizadas
Para construir consultas parametrizadas, utiliza-se o
sinal ? dentro da cláusula SQL usada na criação de
PreparedStatement.
Os parâmetros recebem uma numeração sequencial
(1,2,3,...). O número associado a um parâmetro é
utilizado posteriormente para associar valores para
a execução da cláusula SQL.
53
Exemplo
Supondo que já exista uma conexão aberta
com, abaixo tem-se uma consulta SQL do
tipo update, dependendo de dois
parâmetros:
PreparedStatement pstmt = con.prepareStatement( "UPDATE
table4 SET m = ? WHERE x = ?");
54
Como atribuir valores aos parâmetros ?
Valores são atribuídos aos parâmetros
utilizando-se métodos do tipo set*(ordem
do parâmetro, valor).
Estes métodos são invocados a partir do
objeto PreparedStatement criado.
55
Exemplo
...
PreparedStatement pstmt = con.prepareStatement( "UPDATE
table4 SET m = ? WHERE x = ?");
pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);
...
56
Como executar uma PreparedStatement?
Como acontece com a classe Statement,
depende do tipo de cláusula SQL utilizada.
Por exemplo, uma cláusula do tipo
UPDATE é feita da seguinte maneira:
PreparedStatement pstmt = con.prepareStatement( "UPDATE
table4 SET m = ? WHERE x = ?");
pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);
pstmp.executeUpdate();
57
Mapeamento de tipos
O mesmo cuidado tomado quando se se
utilizava leitura de valores de campos com
os métodos get*(campo) com relação aos
mapeamento de tipos também deve ser
tomado quando se utiliza métodos
set*(ordem do parâmetro, valor).
58
Tabela de mapeamento de tipos
59
Associação de valores NULL
Para se associar NULL a um valor de parâmetro,
utiliza-se o método setNull disponibilizado pela
PreparedStament.
public void setNull(int parameterIndex,int sqlType)
throws SQLException
O
segundo parâmetro é representados pelas
constantes da classe Types, do pacote java.sql.
60
Exemplo
...
PreparedStatement pstmt = con.prepareStatement( "UPDATE
table4 SET m = ? WHERE x = ?");
pstmt.setLong(1, 123456789);
// Segundo parâmetro NULL num campo tipo TIME
pstmt.setNull(2,java.sql.Types.TIME);
...
61
Associação de Large Objects (LOB)
Para se associar large objects a parâmetros
utiliza-se métodos do tipo set*Stream:
 setBinaryStream: para leitura de
objetos binários
 setAsciiStream: para leitura de
caracteres ASCII
 setUnicodeStream: para leitura de
caracteres Unicode
62
Exemplo
...
File file = new java.io.File("/tmp/data");
int fileLength = file.length();
InputStream fin = new java.io.FileInputStream(file);
PreparedStatement pstmt = con.prepareStatement( "UPDATE
Table5 SET stuff = ? WHERE index = 4");
pstmt.setBinaryStream (1, fin, fileLength);
pstmt.executeUpdate();
...
63
Stored Procedures (I)
Stored procedures são procedimentos
armazenados no servidor de BD para um
determinado fim.
A execução de stored procedures geralmente é
mais rápida, pois o procedimento já está
armazenado no servidor e já de forma précompilada.
64
Stored Procedures (II)
É possível passar parâmetros e receber
parâmetros ou conjunto de dados de uma
stored procedure.
Os parãmetros de uma stored procedure são
classificados em três tipos:
 IN
: somente de entrada
 OUT
: somente de saída
 IN/OUT : entrada/saída
65
Classe CallableStatement
Classe que possibilita a invocação de stored
procedures em JBDC.
Atráves de métodos da classe é possível
passar a consultar parâmetros.
Sua “instanciação” também é feita com base
em uma conexão e os mesmos cuidados
com mapeamento de tipos também devem
ser tomados .
66
Exemplo
CallableStatement cstmt = con.prepareCall( "{call reviseTotal(?)}");
cstmt.setByte(1, 25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);
67
Transações (I)
Através da “instância” Connection é possível
controlar transação via JDBC.
Quando Connection é criada, o esquema de
controle de transações é sempre autocommit, ou seja, terminada a execução de
um statement, um sinal de commit é
enviado. Cada statement é comitado
individualmente e cada transação consiste
de um único statement.
68
Transações (II)
Quando o mecanismo de auto-commit é
desabilitado ( método setAutoCommit(false)),
a transação não termina até que o método
commit() ou rollback() tenha sido invocado.
É possível, ainda, definir o nível de isolamento
das
transações,
através
do
método
setTransactionIsolation(nível). O nível de
isolamento é definido através de constantes da
classe Connection.
Download