Baixe o app para aproveitar ainda mais
Prévia do material em texto
Introdução • SGBDs são utilizados para a implementação de bancos de dados; • Como permitir que aplicações Java possam acessar estes bancos de dados de forma conveniente? Introdução • O padrão ODBC: � Open Database Connectivity; � Solução proposta em 1992 pela Microsoft; � Permite a conectividade de aplicações com vários SGBDs convencionais; �Para várias linguagens de programação; � Implementado na linguagem C; Introdução • Interação entre aplicação e bancos de dados via ODBC; Aplicação ODBC Driver Introdução • Problemas da solução ODBC para aplicações java; � Diferença de linguagem de programação; � Problemas comuns com a chamada de métodos nativos; � Segurança, robustez, portabilidade, etc; � Java não tem a noção explícita de ponteiros; JDBC • API proposta pela Sun em 1997; • Voltada especificamente para aplicações java; • Permite que aplicações java acessem dados tabulares; � Especialmente bancos de dados relacionais; JDBC • Oferece acesso uniforme a uma grande quantidade de SGBDs; � MySQL, SQL Server, Oracle, Postgre SQL, etc; • Oferece portabilidade para as aplicações Java; � A mesma aplicação pode interagir com diferentes SGBDs; JDBC • Com JDBC podemos: � Estabelecer conexões (locais ou remotas) com SGBDs; � Executar comandos SQL DDL e DML; � Receber e manipular resultados de consultas; JDBC • Com JDBC podemos: � Atualizar o banco de dados; � Executar procedimentos armazenados; � Executar transações; JDBC • Com JDBC podemos: � Recuperar informações sobre metadados; � E várias outras coisas.... JDBC • A API JDBC é composta por quatro versões: � JDBC 1.0; � JDBC 2.0; � JDBC 3.0; � JDBC 4.0; JDBC • JDBC 1.0: � Lançada em 1997, com a primeira versão da linguagem java; � Oferece as funcionalidades básicas de acesso a bancos de dados relacionais; � Conexões com bancos de dados e execução de comandos SQL DDL e DML; JDBC • JDBC 2.0: � Lançada em 1998; � O escopo da API foi ampliado para prover suporte a aplicações mais avançadas; � Suporte para características requeridas por servidores de aplicação; JDBC • JDBC 3.0: � Lançada em 2001; � O objetivo desta versão era complementar a API com pequenas funcionalidades que ainda não eram oferecidas; �Suporte a savepoints, reuso de prepared statements em pools de conexões, pools de conexão configuráveis, etc; JDBC • JDBC 4.0: � Lançada em 2011, com dois objetivos principais: � Facilitar o desenvolvimento para todos os desenvolvedores que trabalham com SQL na plataforma java; � Prover funcionalidades para expor a API JDBC para ferramentas e APIs mais poderosas que gerenciam fontes de dados; JDBC • Uma “conversa” entre uma aplicação java e o SGBD via JDBC é normalmente realizada em diversas etapas; � Carregar o driver JDBC; � Estabelecer uma conexão com o SGBD; � Executar ações no banco de dados; � Liberar os recursos utilizados; � Fechar a conexão; Carregando o driver JDBC • Uma aplicação java pode se conectar a um banco de dados de duas formas: � Através de um driver JDBC; � Através de uma ponte JDBC-ODBC; Carregando o driver JDBC • Usando um driver JDBC: � Utilização um driver que implementa a API JDBC para um SGBD específico; � É a melhor forma de se utilizar a API; � Hoje, existem drivers JDBC para praticamente todos os principais SGBDs; Carregando o Driver JDBC • Usando uma ponte JDBC-ODBC: � As ações JDBC são convertidas para um driver ODBC, que executa as ações no SGBD; � Solução útil quando não havia um driver JDBC disponível para o SGBD; � Este tipo de conexão deve ser evitada; Carregando o Driver JDBC • Para carregar o driver JDBC usamos o método forName da classe Class; • Como parâmetro de entrada, informamos o caminho da classe que implementa o driver; � O nome do caminho deve ser totalmente qualificado; Carregando o Driver JDBC • Exemplo: � Class.forName(“com.informix.jdbc.IfxDriver”); Nome da classe referente ao driver JDBC Carregando o Driver JDBC • Importante: � O nome da classe varia de acordo com o driver utilizado; � O driver deve estar presente no classpath da aplicação; Carregando o Driver JDBC • Caso a conexão seja realizada através de uma ponte JDBC-ODBC, a classe JdbcOdbcDriver deve ser usada; • Exemplo: • Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); Carregando o Driver JDBC • Importante: A partir da versão 4.0, não é mais necessário carregar o driver JDBC explicitamente; � Basta adicionar o driver ao classpath da aplicação; Estabelecendo uma conexão • Para que uma aplicação possa se comunicar com um banco de dados, ela deve estabelecer uma conexão com o mesmo; Aplicação Conexão Estabelecendo uma conexão • A conexão é o canal de comunicação entre a aplicação e o banco de dados; • Uma aplicação pode ter mais de uma conexão aberta com o banco de dados simultaneamente; • Uma aplicação pode abrir conexões simultâneas com outros bancos de dados; Estabelecendo uma conexão • Podemos estabelecer uma conexão com um banco de dados de duas formas: � Através da classe DriverManager; � Através da interface DataSource; Estabelecendo uma conexão • Usando a classe DriverManager: � Classe básica para a criação de conexões com bancos de dados; � Foi introduzida na versão 1.0; � Uma das implementações do método getConnection pode ser usada para estabelecer a conexão; Estabelecendo uma conexão • Usando a classe DriverManager: � As informações básicas para a obtenção de uma conexão são: �A URL do banco de dados; �O nome do usuário; �A senha de acesso; Estabelecendo uma conexão • Usando a classe DriverManager: � Uma forma de se obter uma conexão com um banco de dados: Estabelecendo uma conexão • Usando a classe DriverManager: � Entretanto, podemos customizar as propriedades desejadas para a conexão; � Estas propriedades são configuradas em um objeto do tipo Properties; � Usamos o método setProperty para definir o nome e o valor de uma propriedade da conexão; Estabelecendo uma conexão • Usando a classe DriverManager � Outra forma de se obter uma conexão: Estabelecendo uma conexão • Usando a classe DriverManager: � A primeira forma é mais simples, mas a segunda permite configurar várias opções para a conexão; �Uso de SSL, versão do protocolo, codificação de caracteres, etc; Estabelecendo uma conexão • Os mesmos procedimentos podem ser usados para um acesso via uma ponte JDBC-ODBC; • A única diferença é o valor da URL, que deve ter a seguinte forma: jdbc:odbc:NomeDaFonteOdbc; Estabelecendo uma conexão • Usando a interface DataSource: � Introduzida a partir da versão 2.0; � Define as características de uma fonte de dados que pode ser acessada por uma aplicação; �Não é necessariamente um banco de dados; � Esta forma é mais genérica e omite da aplicação os detalhes sobre o acesso aos dados; Estabelecendo uma conexão • Usando a interface DataSource: � Esta forma é mais utilizada para a implementação de aplicações corporativas; �Os objetos podem ser registrados/recuperados usando a API java JNDI; � Nesta disciplina, vamos nos ater às conexões estabelecidas com a classe DriverManager; Estabelecendo uma conexão • A interface Connection: � Define o comportamento de objetos que representam uma conexão entre a aplicação e um banco de dados; � Todas as alternativas de conexão retornam um objeto deste tipo; Estabelecendo uma conexão • A interface Connection: � Com objetos que implementam esta interface, podemos realizar operações como: �Criar objetos para a execução decomandos SQL; �Recuperar informações sobre os metadados; �Recuperar/alterar os parâmetros de configurações da conexão; Executando comandos SQL • A interface Statement: � Interface que permite que uma aplicação execute comandos SQL dentro do banco de dados; � Com ela, podemos executar comandos SQL DDL e DML; � E também configurar alguns parâmetros para a realização de consultas; Executando comandos SQL • A interface Statement: � Os objetos desta interface são criados a partir do objeto que representa a conexão; � Com um Statement, podemos executar vários comandos SQL; �Mas cada Statement só pode manipular um resultado de consulta por vez; Executando comandos SQL • Mais adiante, discutiremos a execução de comandos SQL com mais detalhes; Liberando os Recursos • Ao terminar de interagir com o banco de dados, o programa deve liberar todos os recursos utilizados; � Fechando todos os statements criados durante a conexão; � Encerrando a conexão; Liberando os Recursos • Os recursos são liberados através da invocação do método close; � No objeto que representa o Statement; � No objeto que representa a conexão; Manipulando Exceções • Exceções geradas durante a interação com o banco de dados são encapsuladas em objetos do tipo SQLException; • Esta exceção nos fornece informações para identificar o problema ocorrido; � Com sua possível causa; Manipulando Exceções • Informações obtidas a partir de uma SQLException; � Message; � SQL state code; � Error code; � Cause; � Next Exception; Executando comandos SQL DDL • Como já vimos, comandos SQL são executados a partir de um objeto da interface Statement; • Comandos SQL DDL podem ser executados através do método executeUpdate; • O comando SQL a ser executado deve ser passado como parâmetro; Executando comandos SQL DDL • O método executeUpdate retorna um número inteiro; • Caso seja executado um comando DML, o método retorna o número de tuplas afetadas pela execução; • Caso contrário, o método retorna o valor 0; Executando comandos SQL DDL Exemplo: Criando uma nova tabela Executando comandos SQL DDL Alterando uma tabela já existente Inserindo Dados • Assim como os comandos DDL, a inserção de dados em uma tabela também é feita através do método executeUpdate; • O mesmo método também pode ser usado para alterar e remover tuplas; Inserindo Dados • Exemplo: Adicionando um novo fornecedor; Atualizando Dados • Exemplo: Atualizando tuplas e contando o número de atualizações: Executando Consultas • Em um statement, consultas são realizadas através do método executeQuery; • O código SQL referente à consulta deve ser passado como parâmetro; • O resultado é sempre um objeto do tipo ResultSet; Executando Consultas • Estrutura de um ResultSet: Executando Consultas • A classe ResultSet oferece uma série de métodos que nos permitem mover o cursor de várias formas; • O ResultSet default só permite movimentar o cursor para frente; � E não permite atualizações; Executando Consultas • Entretanto, um Scrollable ResultSet pode ser criado; � Permitindo que o cursor seja movimentado em ambas as direções e de diversas formas; � Permitindo que o resultado seja atualizado; � Podemos configurar o tipo de ResultSet desejado no momento em que o Statement é criado; Executando Consultas • Criando um Scrollable ResultSet: Executando Consultas • O primeiro parâmetro indica o tipo do result set e os valores possíveis são: � TYPE_FORWARD_ONLY; � TYPE_SCROLL_INSENSITIVE; � TYPE_SCROLL_SENSITIVE; Executando Consultas • O segundo parâmetro indica se o result set poderá realizar atualizações e os valores possíveis são: � CONCUR_READ_ONLY; � CONCUR_UPDATABLE; Executando Consultas • O terceiro parâmetro indica se os cursores deverão ser mantidos após uma transação: � HOLD_CURSORS_OVER_COMMIT; � CLOSE_CURSORS_AT_COMMIT; Executando Consultas • Métodos usados para a movimentação do cursor: � next(); � previous(); � first(); � last(); Observação: Todos estes métodos retornam um valor booleano; Executando Consultas • Métodos usados para a movimentação do cursor: � beforeFirst(); � afterLast(); � absolute(int pos); � relative(int pos); Executando Consultas • Percorrendo o resultado de uma consulta: Executando Consultas • Para ler os valores de uma tupla, usamos um método getXXX apropriado valor a ser lido; • O método recebe como parâmetro o nome ou o índice da coluna a ser lida: � getString(“Matricula”); � getString(1); Executando Consultas • Exemplo de código para ler os valores de uma tupla: Executando Consultas • Exemplo de código lendo os valores de uma tupla: Executando Consultas • Tabela de mapeamento de tipos SQL/Java: Executando Consultas • Se o ResultSet é atualizável, podemos inserir novas tuplas; • O método moveToInsertRow é usado para criar a nova linha; • Os métodos updateXXX são usados para configurar o valor de cada atributo; Executando Consultas • No final, o método insertRow é usado para gravar a nova linha no banco de dados; Executando Consultas • Criando uma nova tupla: Executando Comandos Pré- Compilados • São normalmente usados quando desejamos executar comandos com parâmetros cujos valores variam em tempo de execução; • Os valores dos parâmetros são definidos fora do código da consulta; Executando Comandos Pré- Compilados • O string da consulta fica mais limpo e mais fácil de ser entendido; • Consultas pré-compiladas são executadas através de um PreparedStatement; Executando Comandos Pré- Compilados • Marcadores de parâmetro são usados para descrever os parâmetros cujo valor será definido posteriormente; • Marcadores de parâmetro são especificados através do símbolo ?; Executando Comandos Pré- Compilados • Para criar um comando pré-compilado, usamos o método prepareStatement da interface Connection; • O string do comando é passado como parâmetro de entrada; • Configurações do ResultSet também podem ser aplicadas; Executando Comandos Pré- Compilados • Para definir o valor de um parâmetro, usamos o método setXXX apropriado para o valor do mesmo; • Cada método tem dois parâmetros: � A posição do parâmetro a ser alterado; � O valor que deve ser atribuído ao mesmo; Executando Comandos Pré- Compilados • Antes do comando ser executado, todos os valores de todos os marcadores de parâmetro precisam ser definidos; • O método clearParameters pode ser usado para limpar os valores de todos os parâmetros; Executando Comandos Pré- Compilados • Comandos pré-compilados podem ser executados através de três métodos; � executeUpdate(); � executeQuery(); � execute(); Executando Comandos Pré- Compilados • Exemplo de uma consulta pré- compilada; Invocando Procedimentos Armazenados • Procedimentos armazenados são executados através de objetos da interface CallableStatement; � Que estende a PreparedStatement; • Estes objetos representam uma forma padronizada para a execução de procedimentos armazenados; � Independentemente do SGBD; Invocando Procedimentos Armazenados • Invocando um procedimento que não retorna resultado; {call nome_procedimento (?, ?, ..., ?)} • Invocando um procedimento que retorna resultado; {? = call nome_procedimento (?, ?, ..., ?)} Invocando Procedimentos Armazenados • Invocando um procedimento que não tem parâmetros de entrada; {call nome_procedimento} • Procedimentos podem ter três tipos de parâmetros: � Entrada, saída e entrada/saída; InvocandoProcedimentos Armazenados • Parâmetros de entrada: � Os valores dos parâmetros de entrada são definidos de forma semelhante a um PreparedStatement; � Exemplos: �stat.setString(1, “João"); �Stat.setInt(2, 10); Invocando Procedimentos Armazenados • Parâmetros de saída: � Parâmetros de saída são definidos através do método registerOutParameter; �Para cada parâmetro, deve ser configurado o seu tipo JDBC; � Depois da execução, o valor do parâmetro de saída pode ser obtido através de um método getXXX; Invocando Procedimentos Armazenados • Parâmetros de saída: � Definido parâmetros de saída: stat.registerOutParameter(1,java.sql.Types.TINYINT); stat.registerOutParameter(2, java.sql.Types.DECIMAL, 3); � Obtendo valores de parâmetros de saída: byte x = stat.getByte(1); BigDecimal n = stat.getBigDecimal(2); Invocando Procedimentos Armazenados • Parâmetros de entrada e saída: � Para definir um parâmetro de entrada e saída, devemos configurar o valor de entrada e registrar o tipo de saída; � Usando os mesmos métodos usados para a configuração de parâmetros só de entrada e só de saída; Invocando Procedimentos Armazenados • Parâmetros de entrada e saída: � Definido um parâmetro de entrada e saída: CallableStatement cstmt = con.prepareCall( "{call reviseTotal(?)}"); cstmt.setByte(1, (byte)25); cstmt.registerOutParameter(1, java.sql.Types.TINYINT); Invocando Procedimentos Armazenados • Parâmetros de entrada e saída: � Obtendo o valor de um parâmetro de entrada e saída: byte x = cstmt.getByte(1); Invocando Procedimentos Armazenados • Um CallableStatement pode ser criado através do método prepareCall da interface Connection; • O string de chamada do procedimento deve ser passado como parâmetro; Invocando Procedimentos Armazenados • Podemos invocar o procedimento armazenado através de três métodos; � execute(); � executeQuery(); � executeUpdate(); Invocando Procedimentos Armazenados • Executando um procedimento armazenado: Invocando Procedimentos Armazenados • Executando um procedimento armazenado: Recuperando Metadados sobre um Resultado • Podemos recuperar metadados sobre o resultado de uma consulta; • Para isto, invocamos o método getMetadata do objeto que representa o ResultSet; • O resultado é um objeto do tipo ResultSetMetaData; Recuperando Metadados sobre um Resultado • Alguns métodos disponibilizados por esta interface; � getColumnCount(); � getColumnName(int column); � getColumnType(int column); � getPrecision(int column); � isAutoIncrement(int column); � isNullable(int column); Recuperando Metadados sobre um Resultado • Exemplo: Recuperando Metadados sobre o Banco de Dados • Podemos também recuperar metadados sobre o banco de dados; • Para isto, invocamos o método getMetadata do objeto que representa a conexão; • O resultado é um objeto do tipo DatabaseMetaData; Recuperando Metadados sobre o Banco de Dados • Os metadados de uma fonte de dados são representados pela interface DatabaseMetaData; • Os seus métodos permitem recuperar informações acerca da fonte de dados; • Aplicações podem usar esta interface para saber como interagir com a fonte de dados; Recuperando Metadados sobre o Banco de Dados • A interface DatabaseMetaData oferece mais de 150 métodos, que fornecem: � Informações gerais sobre a fonte de dados; � Informações sobre as capacidades da fonte de dados; � Informações sobre os limites da fonte de dados; Recuperando Metadados sobre o Banco de Dados • A interface DatabaseMetaData oferece mais de 150 métodos, que fornecem: � Informações sobre os objetos SQL oferecidos pela fonte de dados e os atributos destes objetos; � Informações sobre o suporte a transações oferecido pela fonte de dados; Recuperando Metadados sobre o Banco de Dados • Alguns métodos desta interface: � getDatabaseProductName(); � getDatabseProductVersion(); � getSchemas(); � getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types); Recuperando Metadados sobre o Banco de Dados • Alguns métodos desta interface: � getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePatt ern); � getProcedures(String catalog, String schemaPatte rn, String procedureNamePattern); � getPrimaryKeys(String catalog, String schema, Str ing table); RowSets • Tipo de objeto que representa um container para dados tabulares; � Estendendo o result set default; • Na prática, permitem que os result sets sejam usados de forma mais fácil e flexível; RowSets • Vantagens oferecidas por RowSets: � Compartilham as funcionalidades oferecidas pelos result sets; � Funcionam como componentes Java Beans; � Oferecem mecanismos para a notificação de eventos; RowSets • RowSets podem ser classificados em dois tipos: � Conectados; � Desconectados; RowSets • RowSets conectados: � Mantém sempre uma conexão aberta com o banco de dados; � A conexão é aberta através de um driver JDBC; RowSets • RowSets desconectados: � Abrem a conexão apenas na hora de recuperar ou atualizar dados; � Feita a operação, a conexão é encerrada, mas pode ser reaberta sob demanda; RowSets • RowSets desconectados: � Podem ser serializados e transmitidos através da rede; � RowSets desconectados são mais leves, sendo apropriados para uso em dispositivos com pouca capacidade de memória; RowSets • A API JDBC oferece cinco implementações para a interface RowSet: � Conectados: JdbcRowSet; � Desconectados: CachedRowSet, WebRowSet, FilteredRowSet e JoinRowSet; JdbcRowSet • Corresponde a um result set melhorado; • Mantém uma conexão aberta com o banco de dados; • Permite tornar um result set navegável e atualizável; JdbcRowSet • Podemos criar uma instância de um JdbcRowSet de várias formas: � A partir de um result set; � A partir de uma conexão; JdbcRowSet • Podemos criar uma instância de um JdbcRowSet de várias formas: • A partir de um construtor default de uma implementação default oferecida pela API; • A partir da instância de uma RowSetFactory; JdbcRowSet • Usamos o métodos setUrl, setUsername e setPassword para configurar as propriedades da conexão; • Usamos o método setCommand para definir a consulta que deve ser executada no banco de dados; JdbcRowSet • Usamos o método execute para executar a consulta no banco de dados; • O objeto é preenchido com dados somente após a execução da primeira consulta; JdbcRowSet • Após executar a consulta, usamos os métodos da interface ResultSet para navegar no resultado e para realizar atualizações; Exemplo de uma consulta usando um JdbcRowSet Usando um JdbcRowSet para executar uma consulta pré-compilada CachedRowSet • Armazena na memória as linhas recuperadas a partir da execução de uma consulta; • Estes objetos fecham a conexão com a fonte de dados após a leitura; � Mas podem abrir novas conexões para reexecutar a consulta e fazer atualizações; CachedRowSet • Um CachedRowSet é normalmente apropriado para: � Passar resultados de consultas para clientes leves; � Transmissão de dados entre componentes de uma aplicação distribuída; Usando um CachedRowSet para recuperar dados CachedRowSet • Uma vez executada a consulta, podemos fazer modificações localmente nos dados do rowset; � Usando os métodos da interface ResultSet; • O método acceptChanges pode ser usado para propagar as atualizações realizadas para o banco de dados; CachedRowSet • Podemos executar a consulta com JDBC e povoar o CachedRowSet como resultado; • O método populate é usado para esta operação; � O resultado da consulta é passado como parâmetro de entrada; Criando e povoando um CachedRowSet WebRowSet • Estende o CachedRowSet, adicionando a capacidade de ler e escrever dados em formato XML; • O método writeXml é usado para imprimir os dados em um arquivo XML; • O método readXml é usado para ler os dados a partir de um arquivo XML; Exemplo de utilização de um WebRowSet FilteredRowSet • Estende o CachedRowSet, permitindo que a aplicação filtre as linhas armazenadas pelo RowSet; • Possibilita a filtragem dos resultados sem ter que realizar uma nova leitura da fonte de dados; FilteredRowSet • Para usarmos este tipo de RowSet, precisamos antes definir um ou mais filtros; • Cada filtro deve implementar a interface Predicate; FilteredRowSet • A interface Predicate define três métodos; � evaluate(RowSet rs); � evaluate(Object value, int column); � evaluate(Object value, String columnName); Exemplo de implementação de um filtro FilteredRowSet Exemplo de uso de um FilteredRowSet JoinRowSet • Permite realizar a combinação de mais de um rowset em um único objeto; • A combinação é feita através de uma junção interna; • Qualquer tipo de rowset pode ser combinado; � Mas geralmente são usados CachedRowSets; JoinRowSet • Usamos o método addRowSet para adicionar um novo rowset ao objeto; • Ao incluirmos um novo rowset, devemos informar o nome ou o índice da coluna que deve ser usada para fazer a junção; Exemplo de uso de um JoinRowSet JoinRowSet • Imprimindo os nomes dos empregados do departamento de vendas: Para Finalizar • O que vimos aqui foi apenas o básico da API JDBC; � Muita coisa legal ainda pode ser feita; • Aprofunde o seu conhecimento;
Compartilhar