Aula 20: JDBC (I) Diego Passos Universidade Federal Fluminense Técnicas de Projeto e Implementação de Sistemas II Diego Passos (UFF) JDBC (I) TEPIS II 1 / 33
JDBC: Introdução Especificação que provê acesso de mais baixo nível a sistemas de Bancos de Dados. Nível mais baixo de abstração que o JPA. Desenvolvedor precisa escrever código SQL para consultas e atualizações. Provê suporte a uma grande gama de bancos de dados. Bancos de dados relacionais. API considerada corporativa, já que aplicações corporativas geralmente acessam bancos de dados. Diego Passos (UFF) JDBC (I) TEPIS II 2 / 33
JDBC: Introdução (II) Apesar de ser considerada uma API corporativa, o JDBC hoje faz parte do J2SE. Desde a versão 3.0 da especificação. Boa parte da API se encontra no pacote java.sql. Mas alguns recursos estão no pacote javax.sql. Diego Passos (UFF) JDBC (I) TEPIS II 3 / 33
JDBC: Arquitetura SGBDs diferentes são geralmente bastante distintos. Embora os SGBDs mais usados sejam relacionais, eles diferem em implementação e recursos. Além disso, cada SGBD provê sua própria API. Escrever aplicações portáveis entre SGBDs é geralmente difícil. Existem APIs que suportam múltiplos Bancos de Dados. ODBC da Microsoft, por exemplo. Mas estas em geral só rodam em plataformas específicas. Diego Passos (UFF) JDBC (I) TEPIS II 4 / 33
JDBC: Arquitetura (II) JDBC foi uma tentativa da Sun de criar uma interface multi-plataforma entre programas Java e Bancos de Dados relacionais. Se um dado Banco de Dados é suportado pelo JDBC, garante-se suporte a determinadas funcionalidades básicas. Entre elas, suporte ao SQL-92. A API encapsula tarefas comuns como: Consultas. Processamento de resultados. Acesso a meta-dados. Acesso a informações de configuração. Diego Passos (UFF) JDBC (I) TEPIS II 5 / 33
JDBC: Arquitetura (III) O JDBC utiliza o conceito de driver. Cada empresa/desenvolvedor de um Banco de Dados pode fornecer um driver JDBC. Conjunto de classes que implementam as interfaces definidas pela especificação. Implementação das especificidades de cada Banco. Uma aplicação pode carregar um ou mais drivers. A partir de configurações de identificação da base de dados, JDBC decide entre os drivers carregados. Múltiplos drivers podem ser usados simultaneamente. Para acessar bases diferentes. Diego Passos (UFF) JDBC (I) TEPIS II 6 / 33
JDBC: Arquitetura (IV) (Fonte: Java Enterprise in a Nutshell) Diego Passos (UFF) JDBC (I) TEPIS II 7 / 33
JDBC: Um Exemplo Básico import java.sql.*; public class JDBCSample { public static void main(java.lang.string[] args) { try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch (ClassNotFoundException e) { System.out.println("Unable to load Driver Class"); return; } try { Connection con = DriverManager.getConnection("jdbc:odbc:companydb", "", ""); Statement stmt = con.createstatement( ); ResultSet rs = stmt.executequery("select FIRST_NAME FROM EMPLOYEES"); while(rs.next()) { System.out.println(rs.getString("FIRST_NAME")); } rs.close(); stmt.close(); con.close(); } catch (SQLException se) { System.out.println("SQL Exception: " + se.getmessage( )); se.printstacktrace(system.out); } } } Diego Passos (UFF) JDBC (I) TEPIS II 8 / 33
JDBC: Um Exemplo Básico (II) Exemplo ilustra uso típico do JDBC. Em primeiro lugar, programa deve carregar o driver apropriado. Isto é feito carregando-se a classe Java correspondente ao driver. No exemplo, é carregado o driver JDBC-ODBC bridge. Como a classe pode não ser encontrada, código pode resultar em exceção. try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch (ClassNotFoundException e) { System.out.println("Unable to load Driver Class"); return; } Diego Passos (UFF) JDBC (I) TEPIS II 9 / 33
JDBC: Um Exemplo Básico (III) Em seguida, abrimos uma conexão com a base de dados. Passamos alguns argumentos que permitem esta conexão. Um deles é uma URL em um formato específico que identifica a base de dados. Connection con = DriverManager.getConnection("jdbc:odbc:companydb", "", ""); Diego Passos (UFF) JDBC (I) TEPIS II 10 / 33
JDBC: Um Exemplo Básico (IV) Uma vez criada a conexão, podemos criar e executar consultas e atualizações. Aqui, fazemos um select simples. O resultado da consulta será colocado em uma estrutura de dados chamada ResultSet. Statement stmt = con.createstatement( ); ResultSet rs = stmt.executequery("select FIRST_NAME FROM EMPLOYEES"); Diego Passos (UFF) JDBC (I) TEPIS II 11 / 33
JDBC: Um Exemplo Básico (V) Esta estrutura de dados nos permite iterar pelas tuplas resultantes da consulta. A API fornece métodos especiais para acessarmos os valores de colunas específicas do resultado. Finalmente, devemos ter o cuidado de desalocar os recursos. while(rs.next()) { System.out.println(rs.getString("FIRST_NAME")); } rs.close(); stmt.close(); con.close(); Diego Passos (UFF) JDBC (I) TEPIS II 12 / 33
JDBC: Objetivo da Disciplina Nesta e nas próximas duas aulas veremos em maiores detalhes o JDBC. O foco é em como usar a API. Tipos de drivers. Como se conectar a uma base. Como fazer consultas e atualizações. Como percorrer os resultados. Como tratar erros. Como lidar com Stored Procedures.... Diego Passos (UFF) JDBC (I) TEPIS II 13 / 33
JDBC: Drivers Os drivers são parte fundamental do JDBC. Dizem como a aplicação deve se comunicar com o BD. Antes de usar um driver, no entanto, deve-se registrá-lo. Há um componente da API chamado DriverManager. Este componente gerencia os drivers. Deve ser informado da existência de um driver antes do uso. Registro geralmente é feito simplesmente carregando a classe do driver. Normalmente com o método Class.forName(). Recebe uma String como argumento. Driver pode ser especificado, por exemplo, como argumento de linha de comando. Diego Passos (UFF) JDBC (I) TEPIS II 14 / 33
JDBC: Drivers (II) Note que quando uma aplicação é executada em um servidor J2EE, o carregamento de drivers pode ser desnecessário. Os servidores podem ser configurados para carregar o drivers e abrir conexões para determinadas bases. Conexões passam a ser recursos disponibilizados pelo JNDI. Para a aplicação, uso é similar ao das EJBs. Acessar o serviço JNDI. Especificar a conexão por um nome conhecido. Utilizar a conexão de forma transparente. Veremos um exemplo mais a frente. Diego Passos (UFF) JDBC (I) TEPIS II 15 / 33
JDBC: Drivers (III) Há drivers JDBC para a maioria dos BDs. Estes vários drivers são geralmente divididos em 4 categorias: Drivers tipo 1: drivers JDBC-ODBC bridge. Drivers tipo 2: drivers parcialmente em Java usando API nativa. Drivers tipo 3: drivers completamente em Java usando protocolo de rede. Drivers tipo 4: drivers completamente em Java usando protocolo nativo. Diego Passos (UFF) JDBC (I) TEPIS II 16 / 33
Drivers Tipo 1 Estes drivers funcionam como uma ponte para sistemas ODBC. O ODBC provê o acesso efetivo ao BD. O driver é apenas um trecho de código em Java que traduz as diretivas para o ODBC. Implicitamente, o ODBC utiliza um outro driver para o acesso efetivo ao BD. Transparente para o código em Java. O driver do tipo 1, em si, é portável. Mas requer software ODBC previamente instalado na máquina. Diego Passos (UFF) JDBC (I) TEPIS II 17 / 33
Drivers Tipo 2 Estes drivers utilizam código nativo de uma API específica do BD para acessá-los. Eles funcionam como uma camada de abstração em Java, escondendo o código nativo. Chamadas JDBC são traduzidas para chamadas do código nativo. Por utilizarem código nativo para o acesso efetivo ao BD: Resultam geralmente em bom desempenho. Podem trazer problemas de portabilidade. Diego Passos (UFF) JDBC (I) TEPIS II 18 / 33
Drivers Tipo 3 São drivers que utilizam uma camada intermediária para acesso ao BD. Esta camada pode estar, por exemplo, em um servidor de aplicação. A comunicação com a camada intermediária é feita através de um protocolo de rede genérico. O middleware (camada intermediária) traduz os comandos JDBC para comandos nativos do BD. O mesmo driver pode ser usado para acessar múltiplos BDs. Assumindo que o middleware os suporte. O driver em si é totalmente escrito em Java. Diego Passos (UFF) JDBC (I) TEPIS II 19 / 33
Drivers Tipo 4 Alguns fabricantes/desenvolvedores fornecem drivers tipo 4. Neste caso, o driver implementa diretamente o protocolo de rede específico do BD. Estes drivers são completamente escritos em Java. Não utilizam softwares intermediários. Por este motivo, são completamente portáveis. Para qualquer plataforma que suporte Java. Diego Passos (UFF) JDBC (I) TEPIS II 20 / 33
Drivers: Resumo Note que, para um mesmo BD, múltiplos drivers podem estar disponíveis. Muitas vezes, um desenvolvedor é obrigado a fazer uma opção por um destes drivers. É preciso pesar os prós e contras. Drivers dos tipos 2 e 4 tendem a ser mais rápidos, pois não há intermédio de outros serviços. Os do tipo 2 podem ser ainda mais eficientes por utilizarem código nativo, mas são menos portáveis. Drivers dos tipos 1 e 3 permitem uma alteração mais fácil entre BDs diferentes. Diego Passos (UFF) JDBC (I) TEPIS II 21 / 33
JDBC: URLs Uma vez carregado o driver adequado, é preciso especificar qual base de dados se deseja conectar. Isto é feito através de uma URL, que especifica informações de identificação e (muitas vezes) autenticação. O formato da URL de uma base de dados varia. Não só dependendo do BD, mas também do driver. Tipicamente, no entanto, a URL tem formato geral jdbc:driver:databasename. Alguns drivers exigem outras informações: jdbc:oracle:thin:@site:port:database. jdbc:odbc:datasource;odbcoptions. Diego Passos (UFF) JDBC (I) TEPIS II 22 / 33
JDBC: Conectando a uma Base de Dados Conexões a bases de dados são encapsuladas por objetos da classe java.sql.connection. Uma mesma aplicação pode manter múltiplas conexões simultâneas. Com várias bases de dados. De vários BDs diferentes. Usando drivers distintos. A criação de uma conexão é feita com o intermédio do DriverManager. Utiliza-se o método getconnection(): Connection con = DriverManager.getConnection("url", "user", "password"); Diego Passos (UFF) JDBC (I) TEPIS II 23 / 33
JDBC: Conectando a uma Base de Dados (II) Na sua forma típica, o método recebe três argumentos: A URL identificando a base a ser acessada. Um nome de usuário. Uma senha. Nome de usuário e senha devem ser credenciais válidas para a conexão com o Banco. Se o Banco não exige autenticação, pode-se passar os dois últimos argumentos como Strings vazias. Diego Passos (UFF) JDBC (I) TEPIS II 24 / 33
JDBC: Conectando a uma Base de Dados (III) O DriverManager pega a URL passada e consulta cada um dos drivers registrados. Perguntando se algum deles entende a URL. Se a URL é reconhecida por um dos drivers, este é usado e um objeto Connection é retornado. Como os drivers são indagados pelo DriverManager é uma boa prática evitar carregar drivers não usados. Diego Passos (UFF) JDBC (I) TEPIS II 25 / 33
JDBC: Conectando a uma Base de Dados (IV) O método getconnection() tem ainda outras duas variantes: getconnection(string). getconnection(string, Properties). A primeira recebe apenas uma URL e tenta conectar ao BD sem nome de usuário e senha. Ou com as credenciais fornecidas na própria URL. A segunda recebe, além da URL, um mapeamento de propriedades. Em geral, possui ao menos as propriedades username e password. Diego Passos (UFF) JDBC (I) TEPIS II 26 / 33
JDBC: Fechando Conexões Ao contrário de outros recursos tipicamente usados em Java, é uma boa prática destruir explicitamente objetos do tipo Connection. Além da memória associada à estrutura de dados, este objeto agrega vários recursos implicitamente: A conexão em si ao BD (que pode limitar o número de conexões simultâneas). Cursores. Descritores de arquivos.... Isto vale para outros objetos da API também (como resultados de consultas). Diego Passos (UFF) JDBC (I) TEPIS II 27 / 33
Objetos DataSource: Método Alternativo de Conexão A interface DataSource provê uma alternativa ao DriverManager para conexões a BDs. Ao invés da aplicação ser responsável por especificar informações de conexão a uma base, esta informação é armazenada em um serviço JNDI. A aplicação se conecta a um serviço JNDI, obtém as informações na forma de um objeto DataSource. Este objeto provê um método para a obtenção de uma conexão à base. No serviço JNDI, um DataSource recebe um nome lógico (configurável). Esta alternativa é geralmente mais portável que o uso do DriverManager. Permite a alteração fácil de driver e base de dados, sem alteração do código da aplicação. Diego Passos (UFF) JDBC (I) TEPIS II 28 / 33
Objetos DataSource: Método Alternativo de Conexão (II) Exemplo de conexão a BD com DataSource: Context ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup("jdbc/cameldb"); Connection con = ds.getconnection("lawrence", "arabia"); Duas primeiras linhas obtêm um DataSource do serviço JNDI. Método getconnection() efetivamente abre a conexão. Diferentemente do DriverManager, só precisamos especificar usuário e senha. Diego Passos (UFF) JDBC (I) TEPIS II 29 / 33
Pooling de Conexões Para aplicações pequenas, usar o DriverManager é mais simples. Não requer configuração de um servidor de aplicação, serviço JNDI,... Mas o uso do DataSource tem suas vantagens: Simplifica o código da aplicação (não é necessário carregar drivers ou conhecer a URL da base). Dá suporte a Connection Pooling: utilizar (e re-utilizar) conexões para distribuir carga das requisições entre elas. Em geral, o pool de conexões é gerenciado pelo servidor de aplicação, de forma transparente para a aplicação. Diego Passos (UFF) JDBC (I) TEPIS II 30 / 33
Pooling de Conexões (II) Neste arquitetura, o driver JDBC cria um conjunto de conexões a as distribui pelos programas, conforme necessário. Quando um programa termina de usar uma conexão, ele a retorna para o pool. Isto elimina (ou reduz) o overhead de criação de conexões. Do ponto de vista dos clientes, seu uso é igual ao de um DataSource normal. Mas é fundamental garantir que a conexão seja devolvida ao pool. Isto é feito, fechando a conexão. Como manipulações de bases de dados podem resultar em exceções, é uma boa prática usar blocos do tipo try/catch/finally: Connection con = null; try { Context ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup("jdbc/cameldb"); Connection con = ds.getconnection("lawrence", "arabia"); // Manipulação da base... } catch(exception e) { //... } finally { if (con!= null) con.close(); } Diego Passos (UFF) JDBC (I) TEPIS II 31 / 33
Próxima Aula Consultas e Atualizações. Manipulação de Resultados. Mapeamento de tipos de dados. ResultSets bidirecionais e auto-atualizáveis. Erros e warnings. Diego Passos (UFF) JDBC (I) TEPIS II 32 / 33
Referências Conteúdo e exemplos baseados em: Jim Farley e William Crawford. Java Enterprise in a Nutshell: A Practical Guide. Terceira edição. Capítulo 8. Diego Passos (UFF) JDBC (I) TEPIS II 33 / 33