Conectividade de Banco de Dados Java JDBC João Carlos Pinheiro jcpinheiro@cefet-ma.br Versão: 3.1 Última Atualização: Abril/2005 1
Objetivos Explicar o que é JDBC Utilizar as classes e interfaces do pacote java.sql para consultar, inserir, atualizar e excluir dados em um banco de dados Explicar como mapear tipos de banco de dados nos tipos Java Criar uma aplicação JDBC 2
Motivação para utilização de BDs Um dos grandes problemas na utilização de arquivos para armazenar dados persistentes é que não oferecem recursos para consultar os dados convenientemente Enquanto que os BDs, além de fornecerem recursos de processamento de arquivos, também organizam os dados de uma maneira que facilita a realização de consultas sofisticadas 3
Vantagens dos Sistemas de Banco de Dados A redundância pode ser reduzida A inconsistência pode ser evitada Os dados podem ser compartilhados Padrões podem ser impostos Restrições de segurança podem ser aplicadas A integridade pode ser mantida Requisitos contraditórios podem ser equilibrados 4
Java Database Connectivity (JDBC) Consiste em um conjunto de classes e interfaces que se destinam a acessar qualquer estrutura tabular de dados a partir de uma aplicação Java 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 Permite que as aplicações sejam independentes com relação ao fabricante do Banco de Dados 5
JDBC A API JDBC padroniza O mecanismo de como deve ser feito a conexão ao Banco de Dados Abordagem para criar consultas Resultado da consulta A API JDBC não padroniza Sintaxe SQL Nota: Na verdade, a especificação JDBC permite que você passe qualquer string para o driver subjacente. 6
JDBC A API JDBC se baseia principalmente em interfaces O papel de cada fabricante é desenvolver drivers para seus gerenciadores de banco de dados específicos, implementando as interfaces definidas Evolução dos drivers JDBC 1ª versão batizada de JDBC Kit (1996) 2ª versão incorporada no Java 2 (1998) 3ª versão (disponível desde o JDK 1.4) 7
Drivers JDBC É considerado o coração do JDBC É uma coleção de classes que implementam as interfaces JDBC necessária para interligar um programa Java a um banco de dados Geralmente é fornecido pelo fabricante do banco de dados (209 drivers catalogados no site da Sun) http://servlet.java.sun.com/products/jdbc/drivers Aplicação Java J D B C Driver Banco de Dados 8
Drivers JDBC Cada driver JDBC deve fornecer uma classe que implemente a interface java.sql.driver Essa classe é utilizada pela classe genérica java.sql.drivermanager quando necessitar de um driver para se conectar a um determinado BD Aplicativo Java (API JDBC) DriverManager Driver JDBC-NET Driver JDBC-ODBC Driver C Driver D 9
Drivers JDBC A função do driver é isolar qualquer código específico do banco de dados da aplicação java Garantindo a independência de plataforma Se você estiver utilizando o SQL padrão e deseja alterar o banco de dados basta trocar o driver a ser utilizado pela aplicação Isto é possível pelo fato da API JDBC ser constituída basicamente de interfaces 10
Tipos e Arquitetura de Drivers JDBC Driver Tipo 1 JDBC-ODBC ODBC Biblioteca DBClient Aplicação/ Applet Java API JDBC Tipo 2 JDBC Driver Manager JDBC-Java Java Partial Driver Biblioteca DBClient SGDB Tipo 3 Pure-Java JDBC Driver Middleware de Banco de Dados Tipo 4 Pure-Java JDBC Driver 11
Drivers JDBC Tipo 1 JDBC-ODBC Funciona como uma ponte entre o JDBC e um driver ODBC Sendo que, para cada máquina cliente onde aplicação estiver executando é necessário a instalação do driver ODBC Por essa razão, esta solução é inadequada para aplicações que não permitem instalar o software no cliente A SUN inclui um driver JDBC/ODBC no JDK 12
Drivers JDBC Tipo 2 JDBC-Java Partial Driver Converte chamadas JDBC em chamadas internas da API do cliente do banco de dados, chamando métodos C ou C++ nativos Os métodos nativos devem residir na máquina onde está instalada a aplicação 13
Drivers JDBC Tipo 3 Pure Java Driver for Database Middleware A API do JDBC acessa uma aplicação intermediária (middleware), encarrega de traduzir chamadas JDBC e enviá-las ao banco de dados É o tipo mais flexível pois a camada servidora do driver pode ser implementada para acessar diversos SGDBs, simplificando a migração entre banco de dados Solução 100% Java no lado cliente 14
Drivers JDBC Tipo 4 Driver 100% Java Converte as chamadas JDBC diretamente para protocolos nativo do SGDB A aplicação cliente pode acessar uma aplicação servidora remotamente e de forma nativa, sem nenhum intermediário Esse tipo de driver quase sempre provém unicamente do fornecedor de banco de dados 15
Alternativas Os drivers dos tipos 1 e 2 não são aconselhável para aplicações que executem em rede, pois necessitam de instalação nas máquinas clientes O desempenho dos drivers tipo 3 e 4 são melhores, também acessam facilmente SGDBs remotos e não acrescentam dificuldades de instalação Os drivers tipo 4 são os mais utilizados 16
Pacote java.sql Existem 8 interfaces associadas ao JDBC DriverManager 0.. * fornece registra 0.. * Driver Legenda Classe Interface Connection Statement ResultSet fornece DatabaseMetaData Cria 0..* Recupera 0..* fornece PreparedStatement ResultSetMetaData CallableStatement 17
Fluxo JDBC DriverManager Driver Driver Statement ResultSet abre a conexão Connection Statement ResultSet Statement Connection executa instruções SQL Processa o resultado Connection Statement ResultSet 18
Acesso a BD com JDBC Para acessar um BD é necessário os seguintes passos: 1. Estabelecer uma conexão com um Banco de Dados (DriverManager - é o gerente de uma conexão JDBC) 2. Enviar instruções SQL (Usando um objeto Statement) 3. Finalmente receber e processar os resultados 19
Estabelecer uma conexão Uma conexão representa uma ligação com determinado banco de dados Uma mesma aplicação pode abrir uma ou mais conexões para um único ou diversos banco de dados São necessário os seguintes passos: 1. Registrar o driver JDBC no DriverManager 2. Criar um objeto do tipo Connection 20
Carregar o driver JDBC É a ponte entre o JDBC e o driver escolhido. Isto é feito inicialmente através de uma chamada ao método Class.forName(<caminhoDoDriver>) que é responsável por carregar o driver solicitado try { Class.forName( sun.jdbc.odbc.jdbcodbcdriver ); } catch(classnotfoundexception e) { } System.err.println(e.getMessage( )); Também pode-se utilizar o método registerdriver() DriverManager.registerDriver( new sun.jdbc.odbc.jdbcodbcdriver()) ; 21
Utilizando um Driver JDBC Caso fosse utilizado o driver MM.mySQl a linha: Class.forName( sun.jdbc.odbc.jdbcodbcdriver ); Seria alterada para Class.forName( org.gtj.mm.mysql.driver ); Esta seria a única mudança na forma de conexão. Todos os outros comandos (exceção também dos parâmetros passados pela URL) seriam idênticos O arquivo.jar contendo a implementação do driver mysql precisa ser colocado em um lugar visível ou adicionado à variável de ambiente classpath. 22
URL JDBC O driver a ser utilizado é informado através de uma URL composta por três partes. jdbc:<subprotocolo>:<dsn> jdbc é o protocolo a ser usado (será sempre jdbc) subprotocol é o nome do driver do servidor de Banco de Dados A aplicação usa o subprotocolo para identificar e selecionar o driver a ser instanciado dsn é geralmente o nome do Banco de Dados a ser acessado 23
URL JDBC Sintaxe dependente do fabricante. Alguns exemplos (jdbc:<subprotocolo>:<dsn> ): jdbc:odbc:locadora jdbc:mysql://localhost:3006/ locadora jdbc:cloudscape:rmi://host:1098/ locadora jdbc:oracle:thin:@200.206.192.216:1521:locadora 24
Realizando uma Conexão JDBC A classe DriverManager manipula objetos do tipo Driver Possui métodos para registrar drivers, removê-los ou listá-los É usado para retornar Connection, que representa uma conexão (sessão) a um banco de dados, a partir de uma URL JDBC recebida como parâmetro String url = "jdbc:odbc:mural"; Connection con = DriverManager.getConnection(url); 25
Statement Oferece meios de passar instruções SQL para o sistema de bancos de dados Chama-se através de uma conexão o método createstatement() para obter um objeto do tipo Statement try { Statement stmt = con.createstatement(); }catch(sqlexception e) { System.err.println(e.getMessage()); } 26
SQLException Ocorre quando há um erro de acesso ao BD, ou seja, uma conexão é interrompida ou o servidor de BD é desligado Os seguintes métodos fornecem mais alguns tipos de informações sobre as exceções: getmessage() retorna uma string descrevendo o erro String getsqlstate() Obtém o estado SQL seguindo as conversões de estado X/OpenSQL int geterrorcode() obtém o código de erro específico do fabricante 27
Statement Através do objeto Statement, pode-se usar métodos para enviar instruções SQL ao BD, como: execute() executa uma instrução SQL arbitrária executequery() para consultas (SELECT) executeupdate() para atualizações (INSERT, UPDATE, DELETE) executebatch() para operações em lote 28
Statement - Exemplos stmt.execute("create TABLE recados (" + "mural_id int NOT NULL," + ); "mural_descricao varchar(100)," + "primary key (mural_id))" int linhasmodificadas = stmt.executeupdate( "INSERT INTO recados " + "(mural_id, mural_nomeusr,mural_descricao)"+ " VALUES (2, 'João', 'Testando')" ); ResultSet rs = stmt.executequery( "SELECT * FROM recados"); 29
ResultSet O método executequery(), da interface Statement, retorna um objeto ResultSet que aponta para conjunto de registros Um ResultSet mantém um cursor apontando para a linha atual de dados esta posicionando inicialmente, antes de sua primeira linha. A chamada a next() torna a primeira linha a atual a segunda chamada torna a segunda linha atual e assim por diante... 30
Métodos de ResultSet para obtenção de dados Método de ResultSet getint(int String) getlong(int String) getfloat(int String) getdouble(int String) getbignum(int String) getboolean(int String) getstring(int String) getdate(int String) gettime(int String) gettimestamp(int String) getobject(int String) Tipo de dados SQL92 INTEGER BIG INT REAL FLOAT DECIMAL BIT CHAR, VARCHAR DATE TIME TIMESTAMP Qualquer tipo (Blob) 31
Métodos de ResultSet para obtenção de dados tipo get<tipo> (int String) Ex: getdouble(int String) O parâmetro do método representa o número ou o nome da coluna desejada try { } Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); c = DriverManager.getConnection("jdbc:odbc:Mural"); stmt = c.createstatement(); rs = stmt.executequery("select * FROM recados"); String strid = ; String strdesc = ; while(rs.next()) { } // Obtém-se o mesmo resultado strusr = rs.getint(1); strdesc = rs.getstring(2); strusr = rs.getint( mural_id ); strdesc = rs.getstring( mural_desc );... O número da coluna inicia em 1 32
ResultSet try { c = DriverManager.getConnection("jdbc:odbc:Mural"); stmt = c.createstatement(); rs = stmt.executequery("select * FROM recados"); while(rs.next()) { //System.out.println(rs.getString(2) + rs.getstring(3)); System.out.println(rs.getString("mural_nomeUsr") + rs.getstring("mural_descricao")); //processar os valores obtidos... } } catch (SQLException e1) { System.err.println("Erro na conexão"); System.err.println(e1.getMessage()); } 33
ResultSet Pode-se navegar pelos registros recuperar obtendo as informações armazenadas nas colunas Os métodos de navegação são next() previous() absolute() first() last() 34
Processamento em Lote Acontece quando várias transações diferentes devem ocorrer independente da interação do usuário Ex: O processo mensal que calcular a folha de pagamento de uma empresa O processamento em lote no JDBC é suportado através do método addbatch() que aceita uma string que é a sql a ser executado como parte do lote 35
Processamento em Lote Statement stmt = connection.createstatement() ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; int[] updatelinhas = stmt.executebatch() ; O primeiro elemento do array contém o número de linhas afetadas na primeira instrução,... E assim sucessivamente. Instruções devem ser de atualização: create, insert, update, delete 36
Fechar Conexão e Exceções Após o uso, os objetos Connection, Statement e ResultSet devem ser fechados. Isto pode ser feito com o método close(): con.close(); stmt.close(); rs.close(); A exceção SQLException é a principal exceção a ser observada em aplicações JDBC 37
PreparedStatement Se as mesmas instruções SQL forem executadas muitas vezes, é vantajoso utilizar um objeto PreparedStatement A pré-compilação ocorre no Banco de Dados PreparedStatment herda da classe Statement para adicionar a capacidade de definir parâmetros dentro de uma instrução 38
PreparedStatement String com instrução SQL é preparado previamente, deixando-se "?" no lugar dos parâmetros Parâmetros são inseridos em ordem, com setxxx() onde XXX é um tipo igual aos retornados pelos métodos de ResultSet 39
PreparedStatement - Exemplo Exemplo String sql= "INSERT INTO Mural VALUES(?,?,?)"; PreparedStatement pstmt = con.preparestatement(sql); pstmt.setint(1, 10); pstmt.setstring(2, João ); pstmt.setstring(3, Isto é um teste ); pstmt.executeupdate();... 40
Transações Permite a execução atômica de comandos enviados ao banco Implementada através dos métodos de Connection commit() rollback() setautocommit(boolean autocommit) Por default, as informações são processadas a medida em que são recebidas. Para mudar: con.setautocommit(false); Agora várias instruções podem ser acumuladas 41
Transações Para processar: con.commit(); Se houver algum erro e todo o processo necessitar ser desfeito, pode-se emitir um ROLLBACK usando: con.rollback(); 42
Transações try { con.setautocommit(false) ; Statement stmt = connection.createstatement() ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; stmt.addbatch("insert INTO recados VALUES( 'João', Mensagem')") ; int[] updatelinhas = stmt.executebatch(); con.commit(); } catch(sqlexception e) { con.rollback(); } 43
Metadados A classe DatabaseMetaData permite obter informações relacionadas ao banco de dados getcolumncout() Obtém o número de colunas que possui uma tabela getcolumname(int i) Obtém o nome da coluna informado pelo i getcolumtype(int i) Obtém o tipo de dado da coluna informado pelo i getcolumlabel(int i) Obtém o nome do label da coluna informada pelo i 44
Metadados Exemplo (1/2) try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); c = DriverManager.getConnection("jdbc:odbc:MURAL"); stmt = c.createstatement(); rs = stmt.executequery("select * FROM recados"); ResultSetMetaData rsmd = rs.getmetadata(); int colunas = rsmd.getcolumncount(); for (int i = 1; i <= colunas; i++) { System.out.print(rsmd.getColumnName(i) + "\t"); } 45
Metadados Exemplo (2/2) while (rs.next()) { for (int i = 1; i <= colunas; i++) { switch (rsmd.getcolumntype(i)) { case Types.INTEGER: System.out.print(rs.getInt(i) + "\t\t"); break; case Types.VARCHAR: System.out.print(rs.getString(i) + "\t\t"); break; } } } // fim do laço while } catch (ClassNotFoundException e) { System.err.println( Erro ao carregar o driver"); } catch (SQLException e1) { System.err.println("Erro na conexão"); } 46