UNIVERSIDADE TIRADENTES - UNIT CENTRO DE CIÊNCIAS FORMAIS E TECNOLOGIA - CCFT CONSTRUÇÃO DE COMPONENTES PARA ACESSO À BANCO DE DADOS USANDO ENTERPRISE JAVA BEANS ALINE SOUTO BEZERRA PROJETO SUPERVISIONADO Aracaju (SE) – Brasil 2001 UNIVERSIDADE TIRADENTES - UNIT CENTRO DE CIÊNCIAS FORMAIS E TECNOLOGIA - CCFT CONSTRUÇÃO DE COMPONENTES PARA ACESSO À BANCO DE DADOS USANDO ENTERPRISE JAVA BEANS Monografia apresentada ao Centro de Ciências Formais e Tecnologia da Universidade Tiradentes. Área: Desenvolvimento de Aplicações Orientador: Prof. Andrés I. Martínez Menéndez ALINE SOUTO BEZERRA PROJETO SUPERVISIONADO Aracaju (SE) – Brasil 2001 DATA 07/12/2001. BANCA _________________________________________ 1º examinador _________________________________________ 2º examinador _________________________________________ 3º examinador ii AGRADECIMENTOS Inicialmente gostaria de agradecer a meu orientador Andrés Ignácio Martínez Menéndez que me propôs este trabalho, me incentivando e apoiando em todo o momento com muita dedicação e paciência. Gostaria de agradecer também a meus amigos inseparáveis Alysson, Danielle, Luana e Norma que estiveram me acompanhando não só neste trabalho, mas em toda a minha vida acadêmica, dividindo comigo minhas alegrias e tristezas. iii SUMÁRIO AGRADECIMENTOS______________________________________________________ iii LISTA DE FIGURAS _______________________________________________________vi RESUMO _________________________________________________________________ vii INTRODUÇÃO _____________________________________________________________ 9 1. ENTERPRISE JAVA BEANS ____________________________________________ 11 1.1 VANTAGENS DO ENTERPRISE JAVA BEANS ___________________ 15 1.2 ARQUITETURA EJB ___________________________________________________ 16 1.2.1 SERVIDOR EJB _____________________________________________________ 17 1.2.2 CONTÊINER EJB ____________________________________________________ 18 1.2.3 TIPOS DE EJB_______________________________________________________ 19 2. COMO CRIAR UM ENTERPRISE JAVA BEAN _________________________ 21 2.1 INTERFACE REMOTA _________________________________________________ 21 2.2 INTERFACE HOME ____________________________________________________ 24 2.3 CLASSE ENTERPRISE BEAN __________________________________________ 25 2.4 DEPLOYMENT DESCRIPTOR __________________________________________ 29 2.5 CLASSE DA CHAVE PRIMÁRIA ________________________________________ 31 3. BEANS DE SESSÃO ____________________________________________________ 33 3.1 TIPOS DE SESSION BEAN _____________________________________________ 33 Session Bean Stateless ___________________________________________________ 35 4. BEANS DE ENTIDADE _________________________________________________ 39 4.1 TIPOS DE ENTITY BEAN _______________________________________________ 40 5. Comparativo entre beans de sessão e beans de entidades. ___________________ 41 6. JDBC (Java DataBase Connectivity) ______________________________________ 44 6.1 TIPOS DE DRIVERS ___________________________________________________ 50 7. SOLUÇÃO PROPOSTA _________________________________________________ 52 8. JSP ( Java server page ) __________________________________________________ 59 9. JBOSS __________________________________________________________________ 61 iv 10. CONCLUSÃO ____________________________________________________________ 63 11. Apêndice 1 – Instalação do JBoss. _______________________________________ 65 12. Apêndice 2 – CLASSES DA API DE ACESSO ___________________________ 68 13. referências bibliográficas _______________________________________________ 82 v LISTA DE FIGURAS Arquitetura EJB 1.......................................................................................................................... 16 Modelo Session Bean e Entity Bean 1 ........................................................................................... 20 Interface Remota 1......................................................................................................................... 22 Arquitetura JDBC 1....................................................................................................................... 46 Funcionamento API de Acesso 1 ................................................................................................... 54 Funcionamento API de Acesso 2 ................................................................................................... 55 vi RESUMO O desenvolvimento de aplicações sofreu enormes mudanças nos últimos anos e o grande responsável por este processo foi o rápido movimento e a demanda no mundo do comércio eletrônico e da tecnologia da informação. Isto fez com que os desenvolvedores de aplicações focassem suas atenções para essas alterações. Estamos vivendo em uma era não mais orientada a um processamento centralizado em computadores de grande porte. Computadores pessoais são as ferramentas de trabalho de todas as corporações e a necessidade da integração entre elas em um ambiente de processamento distribuído é uma realidade. O grande problema atual é a dificuldade de encontrar ferramentas de desenvolvimento capazes de fornecer tudo isso com pouca complexidade para o desenvolvedor. Buscando soluções para estes problemas, a Sun Microsystems desenvolveu uma plataforma de desenvolvimento chamada J2EE (Java 2 Enterprise Edition). Esta tecnologia fornece um modelo de ambiente distribuído em várias camadas; a habilidade de reutilização de componentes; um modelo de segurança unificado e a flexibilidade no controle de transação. Esta plataforma faz uso de APIs (Interface para programação de aplicação), que são bibliotecas voltadas para o desenvolvimento de aplicações corporativas. Porém, muita coisa ainda precisa ser realizada para permitir um desenvolvimento mais rápido de aplicações multicamadas. Um dos problemas encontrados pelos desenvolvedores de aplicações é que a linguagem Java disponibiliza uma API de baixo nível para acesso a dados, elevando o custo e tempo de desenvolvimento do sistema. Para melhorar o quadro atual, este trabalho propõe criar uma interface de acesso. Isto tem como objetivo facilitar a tarefa do desenvolvedor. O mesmo ficará responsável somente pela lógica da aplicação, partindo do princípio que é necessário conhecer apenas quais métodos devem ser chamados para executar uma determinada tarefa. Serão construídos componentes, em Java, que podem ser usados pelos desenvolvedores de software nas suas aplicações de acesso a banco de dados. vii INTRODUÇÃO Para a implementação deste trabalho foi escolhida a plataforma Java por ser um modelo de computação baseado no poder das redes e na idéia de que o mesmo software poderia ser executado em diferentes tipos de computadores, aparelhos ou outros dispositivos. Ela pode ser descrita como uma ferramenta de desenvolvimento de aplicativos que fornece uma linguagem de programação orientada a objetos simples, de alta performance, interpretada e portável. A linguagem Java possui um conjunto de APIs. Estas APIs permitem que os 9 fabricantes de software desenvolvam componentes reutilizáveis que podem ser incorporados pelos usuários finais nos seus programas, através de ferramentas visuais, de construção de aplicativos. Beans são classes escritas em Java, qualquer classe pode ser chamada de Bean, desde que apresente certas propriedades e determinadas conversões para eventos de interfaces, estas propriedades serão vistas no decorrer deste trabalho. Para desenvolver um Bean é disponibilizado pela Sun o BDK (Bean Development Kit) que é uma aplicação escrita em Java que fornece mecanismos de suporte para a construção de JavaBeans, além de incorporar um ambiente de teste para beans desenvolvidos. Neste trabalho iremos construir componentes em Java que possam ser usados pelos desenvolvedores de softwares nas suas aplicações de acesso à banco de dados. Para isso iremos utilizar a API Enterprise Java Bean que estende o modelo JavaBean e é direcionada para a produção de componentes que possam ser distribuídos, escaláveis e que possam ser utilizados em aplicações multicamadas. 10 1. ENTERPRISE JAVA BEANS “Enterprise JavaBeans é uma arquitetura de componentes que visa o desenvolvimento de aplicações distribuídas, baseando-se em componentes. As aplicações projetadas para a arquitetura Enterprise JavaBeans são escaláveis, robustas, gerenciam transações e gerenciam a segurança em ambiente multiusuário. Estas aplicações podem ser escritas uma vez e distribuídas para qualquer plataforma de servidor que suporte a especificação Enterprise JavaBeans”. (Sun Microsystems Enterprise JavaBeansTM specification, v1.1, 1999). Verificando a definição anterior, sobre a arquitetura Enterprise Java Beans, 11 podemos notar a força e a importância que a sua especificação fornece para os desenvolvedores de uma forma geral. Nesta sessão será mostrado o que é a arquitetura EJB e como ela funciona. Enterprise Java Bean é uma tecnologia de desenvolvimento para aplicações que utilizam as tecnologias de desenvolvimento em n-camadas. Ela ajuda na implementação de regras de negócio de uma aplicação empresarial, no acesso à banco de dados ou ainda a sistemas legados. Esta tecnologia de desenvolvimento baseada em Java possibilita o desenvolvimento de componentes que permitem que as aplicações se comuniquem por várias camadas cliente e servidor além de usar Internet e Intranet. Com esses componentes definidos, o desenvolvedor da aplicação final precisa apenas implementar a interface com o usuário, que pode ser um site WEB utilizando-se JavaServer Pages (JSP), Servlets ou mini-aplicativos (Applets); pode ser uma aplicação para dispositivos móveis (WAP); pode também ser uma aplicação desktop no estilo GUI (Graphical User Interface) ou ainda pode ser baseada em caracteres. O EJB é usado quando existe a necessidade de objetos distribuídos 12 comunicarem entre cliente–servidor ou servidor-servidor. A tecnologia EJB representa a junção entre duas tecnologias: (a) os processamentos transacionais (TP), que são ambientes onde rodam aplicações escritas em linguagens procedurais; (b) e os serviços de componentes distribuídos. Combinando o melhor do “processamento transacional” e dos “componentes distribuídos”, a tecnologia EJB provê um misto entre o processamento transacional, com características que reduzem a complexidade no desenvolvimento pelo gerenciamento automático de todo o ambiente programável, incluindo transações, segurança, concorrência e persistência, com as características de componentes distribuídos que fornecem maior produtividade ao desenvolvimento de aplicativos na construção de aplicações a partir de componentes flexíveis e reutilizáveis. O modelo de componente EJB permite ao desenvolvedor de aplicação trabalhar nos aspectos de negócio da aplicação sem ter que focalizar em gerenciamento da transação, segurança, persistência de dados ou a administração do ciclo de vida do objeto. Quando não usamos EJB, o desenvolvedor precisa tomar conta dos seus dados “na mão”, isto é: 13 • Abrir uma conexão com o banco de dados; • É necessário solicitar um registro e traduzi-lo para objetos; • Deve-se monitorar o desempenho e a transação; • Escrever suas rotinas; • Fechar a conexão com o banco de dados. Isso mostra que no passado, os desenvolvedores tinham que escrever suas próprias rotinas de rollback ou commit1, ou pelo menos conhecer as interfaces para complexos sistemas transacionais. Com a tecnologia EJB, os desenvolvedores focam seus esforços na lógica do negócio, ficando as funções de baixo nível a cargo do servidor de aplicação. Com isso o sistema fica menos sujeito a erros e o desenvolvedor é mais produtivo. Enterprise JavaBean roda na camada intermediária de arquitetura em ncamadas. O termo n-camadas é utilizado para denominar uma arquitetura onde podemos ter várias camadas que fazem trabalhos específicos. Normalmente uma camada é composta por clientes Java que acessam os “beans” (applets, JSP, entre outros). Uma outra camada é composta pelos beans propriamente ditos que acessam uma outra camada de acesso a banco de dados 14ROLLBACK: Aborta a transação corrente COMMIT: Executa a efetivação da transação corrente e começa uma nova; (Silberschtz,Abraham;Korth,Henry F.; Sudarshan,S:1999:460) 1 1.1 VANTAGENS DO ENTERPRISE JAVA BEANS 1.1.1 PORTABILIDADE, PERSONALIZAÇÃO E REUTILIZAÇÃO Os componentes EJB são portáveis entre os servidores de aplicativos compatíveis com o EJB de qualquer fornecedor e podem ser executados em qualquer plataforma. Um componente EJB pode ser personalizado para ser executado em um ambiente específico, além do que, uma vez definidos e empacotados eles podem ser reutilizados. 1.1.2 ARQUITETURA PERSISTENTE E INDEPENDENTE As especificações EJB definem um modelo persistente de componente do lado do servidor, e qualquer aplicativo desenvolvido em uma plataforma pode ser distribuído em outra. 15 1.1.3 DESENVOLVIMENTO MAIS RÁPIDO DE APLICATIVO Os provedores de beans concentram-se no desenvolvimento de classes EJB que implementam a lógica comercial e não mais se preocupa com a implementação de serviços de infra-estrutura complexos em seus programas. 1.2 ARQUITETURA EJB A aplicação EJB possui a seguinte arquitetura: Arquitetura EJB 1 16 JNDI (Java Naming and Directory Interface) fornece uma interface única padronizada pela indústria para os serviços de nomenclatura de diretórios, gerando o compartilhamento de uma variedade de informações a respeito de usuários, nas redes, servidores e aplicativos por toda a rede. 1.2.1 SERVIDOR EJB O servidor EJB é um aplicativo que gerência os contêineres EJB e provê acesso aos serviços do sistema, ele é quem executa os beans empresariais2 e fornece objetos de serviço, como serviços de transação e segurança. Os EJBs são executados dentro de um contêiner EJB que fica dentro do servidor EJB. Um servidor EJB pode ser qualquer banco de dados, de aplicativos e de camada central que possa ter um contêiner EJB e fornecer a ele os serviços necessários, como: serviços de localização, controle transacional, segurança, serviço de mensagem e serviços de baixo nível (por exemplo, conectividade de rede). 17 2 Chamo de beans empresariais por que se tratam de beans que representam uma entidade de objeto de negócio 1.2.2 CONTÊINER EJB A principal responsabilidade do contêiner EJB é responder as pesquisas de clientes e tornar os objetos EJB acessíveis para os clientes, fornecendo os seus serviços através de heranças de métodos ou de invocação remota. Os EJBs são componentes que funcionam em contêineres EJBs. Estes contêineres apresentam uma interface uniforme que gerencia as interações entre beans empresariais e servidor. O contêiner EJB gerencia muitas instâncias e muitos componentes EJBs, consistindo-se em várias classes EJBs. Serviços de gerenciamentos de transações e recursos como segurança, gerenciamento de cache e de mensagens, flexibilidade, mobilidade, controle de versões, persistência e conectividade de banco de dados, são fornecidos pelo contêiner EJB para o bean empresarial que ele contém. 18 1.2.3 TIPOS DE EJB Um bean é um componente que pode ser usado independente ou com outro bean para construir um aplicativo. Existem dois tipos: os Session beans e os Entity beans. Os Session beans são uma extensão do aplicativo cliente e os Entity beans são objetos persistentes que representam linhas de uma tabela de banco de dados. Os Entity Beans armazenam dados permanentes, em algum banco de dados ou sistemas de arquivos, e métodos associados para manipulação dos mesmos. Na maioria dos casos, um Entity bean deve ser acessado de alguma forma transacional. Suas instâncias são exclusivas e elas podem ser acessadas por vários usuários. Por exemplo, as informações sobre uma conta bancária podem ser armazenadas em uma instância de um bean, este pode conter uma identificação para a conta, o tipo da conta (corrente ou poupança) e um balanço. Os Session beans armazenam uma ou mais tarefas de negócios e dados que não são permanentes a um cliente em particular. No caso do session bean, como eles não são armazenados em uma fonte de dados permanentes, não existe mal nenhum 19 em se perder estes dados. Porém, isso não significa dizer que um session bean não possa atualizar dados em um banco de dados, ele pode fazer isso acessando um Entity bean ou através de sentenças SQL. Um exemplo para um session bean poderia ser o armazenamento de uma tarefa associada a uma transferência de fundos entre duas contas bancárias. Este Session bean pode encontrar duas instâncias de um Entity bean de conta, que foi o exemplo anterior, e subtrair a quantia especificada de uma conta e adicionar na outra. Iremos dar mais ênfase a estes beans nas sessões posteriores. Abaixo está uma figura que representa o modelo de funcionamento do entity bean e do session bean. CONTAINER EJB EB 1 APP 1 SB 1 EB 2 SGBD APP 2 SB 2 APP 3 EB 3 SB 3 EB 4 Modelo Session Bean e Entity Bean 1 20 2. COMO CRIAR UM ENTERPRISE JAVA BEAN Para implementar um EJB, seja um Entity bean ou um Session Bean, é preciso definir algumas classes e interfaces, com métodos que todo o bean deve prover, além de um arquivo de configuração. Iremos descrever um pouco sobre cada uma delas: 2.1 INTERFACE REMOTA Define os métodos do bean para que a aplicação cliente utilize. Esta interface herda de javax.ejb.EJBObject e segue regras de interfaces remotas RMI, tais como: todos os métodos devem lançar a exceção java.RemoteException; os parâmetros e 21 valores de retorno devem ser tipos primitivos da linguagem; objetos serializáveis ou objetos remotos. As interfaces remotas duplicam todos os métodos da lógica de negócio que existem no bean. Veja sua ilustração abaixo: CLIENTE EJB InterFace Home Enterprise Java Bean InterF ace R em ota Interface Remota 1 O conjunto de métodos de negócios disponíveis ao cliente é definido por esta interface, ela suporta os métodos de negócio do objeto, obtém a interface Home, remove a instância do enterprise bean e obtém um tratamento de uma instância enterprise bean. 22 Para ilustrar como exemplo irei mostrar uma Interface Remota para um Entity Bean que armazena dados de uma conta bancária. Esta interface terá a assinatura dos métodos de negócio necessários para a execução do comando SQL que será passado. package bean.entity; import javax.ejb.*; import java.rmi.*; /** * Interface Remota do Entity Bean * Toda interface remota deve estender a classe EJBObject */ public interface Conta extends EJBObject { /* Definição dos métodos implementados pelo Bean, * onde todos devem lançar a exceção RemoteException. */ public void saque(double valor) throws RemoteException; public void deposito(double valor) throws RemoteException; public double getSaldo() throws RemoteException; public double getSaldoDisponivel() throws RemoteException; public String getNumero() throws RemoteException; public void setNumeroConta( String conta ) throws RemoteException; public void setSaldo( double valor ) throws RemoteException; public String getSenha() throws RemoteException; 23 public void setSenha( String senha ) throws RemoteException; } 2.2 INTERFACE HOME Esta Interface possibilita que o cliente tenha uma referência para o bean, também define os métodos do ciclo de vida de um bean. Ela herda de Javax.ejb.EJBHome. Através dela é possível que um cliente crie e remova uma instância de um Enterprise Bean e tenha um tratamento que suporte um mecanismo de persistência. Está Interface deve definir alguns métodos para criar o bean: • O nome do método deve ser obrigatoriamente "create"; • O retorno deve ser a interface remota do Bean; • O método deve lançar as exceções javax.ejb.CreateException. A Interface Home para o nosso exemplo será construído da seguinte forma: 24 package bean.entity; import import import import javax.ejb.*; java.rmi.*; java.util.Collection; bean.entity.Conta; /** * Interface home do Entity Bean */ public interface ContaHome extends EJBHome { public Conta create(String numero) throws CreateException, RemoteException; /** * Método find para retornar um registro especifico */ public Conta findByPrimaryKey(ContaPK pk) throws FinderException, RemoteException; /** * Método find para todos os registros */ public Collection findAll () throws RemoteException, FinderException; 2.3 CLASSE ENTERPRISE BEAN É na classe do bean que se encontra a implementação dos métodos de negócio definidos na interface remota. O container chama essa classe quando o cliente invoca qualquer um dos métodos especificados. 25 Todo bean deve implementar a interface javax.ejb.EnterpriseBean. Dependendo do tipo será implementada uma interface específica (javax.ejb.SessionBean ou Javax.ejb.EntityBean) que herda de Enterprise Bean. Esta classe deve implementar os métodos de criação e localização listados na interface home e todos os métodos definidos na interface remota, para cada método Create listado na interface Home, deve herdar na classe bean a implementação de um método ejbCreate correspondente. É importante mencionar que uma das provas que o desenvolvedor do bean não tem que se preocupar com aspectos ligados a comunicação entre objetos é que o Bean não deve implementar a interface remota em si, apesar de ter que prover a implementação dos métodos nela declarados. Quem cria a classe que implementa a interface remota é o servidor EJB na criação do container e não o desenvolvedor. Veja exemplo abaixo: package bean.entity; import javax.ejb.*; import java.rmi.*; 26 /** * Classe Bean do Entity Bean */ public class ContaBean implements EntityBean { /** Contexto da classe */ // private Context ctx; //Atributos do bean (são os campos da tabela) public String numeroConta; public double saldo; public String senha; public ContaPK ejbCreate(String numeroConta) throws CreateException, RemoteException { setNumeroConta( numeroConta ); setSaldo( 0 ); return null; } //Métodos declarados na interface do bean public void saque(double valor) throws RemoteException { if (valor > saldo) throw new RemoteException("Saldo Insuficiente"); saldo -= valor; } public void deposito(double valor) throws RemoteException { if (saldo < 0) throw new RemoteException("Valor inválido para depósito"); saldo += valor; } public double getSaldo() throws RemoteException { return saldo; } public double getSaldoDisponivel() throws RemoteException { 27 return saldo - 0.38; } public String getNumero() throws RemoteException { return numeroConta; } public void setNumeroConta( String conta ) throws RemoteException { this.numeroConta = conta; } public void setSaldo( double valor ) throws RemoteException { this.saldo = valor; } public String getSenha() throws RemoteException { return senha; } public void setSenha( String senha ) throws RemoteException { this.senha = senha; } public void ejbPostCreate(String conta) throws RemoteException,CreateException { } public void unsetEntityContext() { } public void setEntityContext( EntityContext ejb) public void ejbStore() { { } public void ejbRemove() } { } public void ejbPassivate() { } public void ejbLoad() } public void ejbActivate() { 28 { } 2.4 DEPLOYMENT DESCRIPTOR É um arquivo XML (Extensible Markup Language) que contém a identificação do bean e todas as propriedades necessárias durante o seu processamento no servidor. Eles permitem que certos componentes sejam personalizados sem alterar a classe do bean ou suas interfaces. Quando a classe do bean e suas interfaces são modificadas, um deployment descriptor para o bean é gerado e preenchido com dados sobre o bean. Geralmente, IDEs (ambientes de desenvolvimento integrado) como o JBuilder da Borland, suportam o desenvolvimento de Enterprise JavaBeans permitindo a geração do deployment descriptor utilizando componentes visuais. Abaixo está um deployment descriptor. Este exemplo contém as definições do Session Bean, as suas classes Remota, Home e do bean, junto com o tipo do bean que neste caso será um Stateless ( que será visto na sessão 3.1). As mesmas definições são aplicadas para o Entity bean só que para eles devem ser declarados todos os campos da tabela e seus métodos. 29 <?xml version="1.0" encoding="Cp1252"?> <ejb-jar> <enterprise-beans> <session> <display-name>Processar</display-name> <ejb-name>ProcessarConta</ejb-name> <home>mono.ProcessarHome</home> <remote>mono.Processar</remote> <ejb-class>mono.ProcessarBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> <entity> <display-name>Recurso</display-name> <ejb-name>RecursoConta</ejb-name> <home>mono.RecursoHome</home> <remote>mono.Recurso</remote> <ejb-class>mono.RecursoBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>mono.RecursoPk</prim-key-class> <reentrant>False</reentrant> <cmp-field> <field-name>codigo</field-name> </cmp-field> <cmp-field> <field-name>descricao</field-name> </cmp-field> <cmp-field> <field-name>comentario</field-name> </cmp-field> </entity> </enterprise-beans> <assembly-descriptor> <container-transaction> <description>Processar</description> <method> <ejb-name>ProcessarConta</ejb-name> <method-name>*</method-name> </method> <method> <ejb-name>RecursoConta</ejb-name> <method-name>*</method-name> 30 </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> 2.5 CLASSE DA CHAVE PRIMÁRIA Esta classe só é usada na construção do Entity bean, nela deve-se definir também uma classe que contem métodos construtor cujos parâmetros são os atributos que compõem a chave primária da tabela. Ela será utilizada para identificar uma determinada instância do bean pelo cliente e servidor. Veja abaixo um exemplo para esta classe: package bean.entity; /** * Classe com a chave primaria do Entity Bean */ public class ContaPK implements java.io.Serializable { //Os atributos que compõem a chave devem ser públicos public String numeroConta; /** * Construtor padrão - Obrigatório para implementação CMP */ public ContaPK() { } /** * Construtor 31 */ public ContaPK(String numeroConta) { this.numeroConta = numeroConta; } /** * Redefinição obrigatória do método herdado de Object */ public boolean equals (Object pk) { if ( pk instanceof ContaPK ) { ContaPK obj = (ContaPK) pk; return obj.numeroConta.equals(numeroConta); } return false; } /** * Redefinição obrigatória do método herdado de Object */ public int hashCode ( ) { return numeroConta.hashCode(); } } 32 3. BEANS DE SESSÃO Um bean de sessão é um objeto usado por um único cliente e não pode ser compartilhado entre vários. Eles representam uma conversação transitória com um único cliente e são executados exclusivamente para ele. Os beans de sessão têm vida relativamente curta, ou seja, sua duração está associada à duração do cliente que os utiliza. Eles podem ser transacionais, dessa forma permitem a manipulação de dados de um banco de dados. 3.1 TIPOS DE SESSION BEAN Existem dois tipos de beans de sessão: Session Bean Stateful O session bean stateful possui um estado (atributos do bean que serão preservados ao longo do seu ciclo de vida). Session beans stateful não representam 33 dados diretamente em um armazenamento persistente, mas eles podem acessar e atualizar dados do cliente. Como o nome sugere, o tempo de vida do session bean stateful é o mesmo que o tempo de vida do cliente. Esta característica é útil quando se quer preservar dados a respeito de um cliente enquanto ele estiver utilizando a aplicação. Um exemplo prático é o carrinho de compras de uma loja virtual. Para cada pessoa que visita o site é criado e mantido um carrinho de compras exclusivo, contendo suas respectivas compras. As seguintes características auxiliam na decisão de utilizar objetos de negócio como session beans stateful: • Manutenção do estado de um cliente específico. • Session beans stateful são projetados para manter o estado de um cliente, portanto objetos de negócio representando a lógica de negócio do cliente devem ser modelados como session beans stateful. Já que instâncias de session beans stateful são presas a um cliente, recursos do sistema utilizados pelos session beans stateful não podem ser compartilhados por múltiplos clientes. • Representação de objetos não-persistentes. • O estado de um session bean stateful não é armazenado persistentemente e não pode ser recriado depois que a sessão do cliente for finalizada. Portanto, objetos de negócio que possuem vida relativamente curta e não persistente 34 devem ser modelados como session beans stateful. • Representação do fluxo de trabalho entre objetos de negócio. Os objetos de negócio que gerenciam a interação dos vários objetos de negócio em um sistema são excelentes candidatos para serem modelados como session beans stateful. Tais objetos geralmente exibem algumas das características citadas anteriormente. Para obter uma melhor performance é utilizado um mecanismo conhecido como Pool de objetos. Um pool é um repositório de instâncias de objetos que permite a reutilização dos objetos sem que seja necessária a criação de novas instâncias. Isso é possível pois nada impede que um session bean stateful, apesar de ser utilizado por um único cliente em um dado momento, possa ser reutilizado por outro cliente após o primeiro encerrar sua sessão. Session Bean Stateless No session bean stateless não há estado a ser preservado ao longo do seu ciclo 35 de vida. Quando o cliente invoca mais de uma vez métodos de uma mesma referência de um bean do tipo stateless, é bastante provável que não seja o mesmo bean no servidor que realizará o processamento em cada método chamado. Como o stateless não possui estado, não haverá nenhum problema se a mesma instância estiver respondendo a vários clientes ao mesmo tempo. Essa diferença de tipos existe apenas para aproveitar melhor a memória da máquina, uma vez que não é necessário ter um número tão grande de objetos stateless no pool quanto os stateful. É importante destacar que constitui um erro se a interface home de um bean stateless possuir um método create com parâmetros, porque não há nenhuma garantia que esses dados passados serão preservados quando for chamado um método desse bean criado. As seguintes características auxiliam na decisão de utilizar objetos de negócio como session beans stateless: • Modelagem de objetos reutilizáveis. • Um objeto de negócio que fornece algum serviço genérico para todos os clientes pode ser modelado como session beans stateless. Tal objeto não 36 necessita manter qualquer informação específica do estado do cliente, assim a mesma instância do bean pode ser reutilizada para outros clientes. • Fornecimento de alta performance. • Um session bean stateless pode ser muito eficiente, pois requer poucos recursos do sistema por não estar preso a nenhum cliente. Com isso, dependendo do servidor EJB, aplicações que usam session beans stateless podem ser mais escaláveis que aplicações que utilizam session beans stateful. Por outro lado, esse benefício pode ser equiparado ao aumento da complexidade da aplicação cliente que usa session beans stateless, porque o cliente tem que executar as funções de gerenciamento do estado. • Operação sobre múltiplas linhas de cada vez. • Um objeto de negócio que manipula múltiplas linhas em uma tabela de banco de dados e representa uma visão compartilhada dos dados é um session bean stateless ideal. Já que todos usuários poderiam estar interessados em tais informações, o session bean stateless que representa isto poderia ser compartilhado. • Fornecimento de visão procedural dos dados . • Em uma visão procedural dos dados, métodos do objeto de negócio não operam sobre variáveis de instância. Em vez disso, eles comportam-se como procedimentos em uma linguagem procedural. 37 A tabela abaixo ilustra as diferenças entre os dois tipos de Session Beans Característica Stateful Session Bean Stateless Session Bean • Não há estado a ser gerenciado • Bean pode sair da memória sem • Gerência de estado Gerenciado salvar qualquer automaticamente pelo coisa Container • Beans são intercambiáveis e podem ser alocados de um "pool" • • O cliente tem que Estado conversacional Responsabilidades manter qualquer pode ser mantido no Bean estado • Performance • Extremamente Mais pesado lightweight 38 4. BEANS DE ENTIDADE Um bean de entidade é um objeto de longa duração que pode ser acessado de uma sessão para outra e permite o compartilhamento por vários clientes. Eles representam dados de dispositivos de armazenamento persistentes, como um banco de dados. Existem várias vantagens em usar entity beans em lugar de acessar o banco de dados diretamente. Usar entity beans para transformar os dados em objetos fornece ao desenvolvedor um mecanismo simples para acessar e modificar estes dados. É mais simples chamar um método membro.setComando() do que executar o comando SQL 39 equivalente. Além disso, transformando os dados em objetos, o software poderá ser mais reutilizável. Uma vez definido o conceito de um entity bean Cliente, ele pode ser reaproveitado em várias situações, porém existe uma limitação no que diz respeito à flexibilidade na manipulação dos dados, pois esta fica restrita aos métodos existentes, além de serem extremamente pesados já que cuidam da persistência no banco de dados. Quando um novo bean é criado, um novo registro é inserido no banco de dados e uma instância do bean é associada com o registro. Assim, quando o estado do bean muda, essa mudança é sincronizada com os dados no banco de dados. 4.1 TIPOS DE ENTITY BEAN CMP (Container - Managed Persistence): para este tipo de entity bean não é necessário escrever o código para acessar o banco de dados, este fica a critério do servidor utilizado. BMP (Bean - Managed Persistence): utilizando este bean o desenvolvedor deve implementar o acesso ao banco de dados. É necessário definir métodos de localização e os de alteração dos dados, estes deverão conter comandos SQL através de JDBC. 40 5. COMPARATIVO ENTRE BEANS DE SESSÃO E BEANS DE ENTIDADES. Abaixo está uma tabela que mostra em detalhes as características de cada bean Característica Session Bean Entity Bean 41 • • BD Uma conversação transiente com um cliente • Dão uma visão OO de um • Representam dados em Pode ser considerado uma um banco de dados e os O que o Bean extensão do cliente que o métodos que agem sobre representa criou os dados • Pode acessar um banco de • Em um SGBDR, cada dados usando JDBC ou Bean poderia representar acessando um Entity Bean um registro de uma tabela, por exemplo • Igual ao tempo de vida do cliente. Existe somente Tempo de vida • Persiste tanto quanto os durante uma sessão dados do banco de dados cliente/servidor (longo espaço de tempo) (curto espaço de tempo) • Representam o estado da • Representam dados de um conversação. Este estado é banco de dados (colunas Atributos mantido entre chamadas a de uma tabela, por métodos, mas não depois exemplo) que a sessão acaba 42 • Persistência • Gerenciada O Bean deve gerenciar sua automaticamente pelo (além da sessão) própria persistência Container • • Acesso compartilhado entre clientes Um único Bean por cliente Compartilhamento (sem compartilhamento) • Container gerencia a concorrência • Identificado usando uma chave primária Criação • No início de uma sessão • A criação de um novo Bean insere dados num banco de dados • Não sobrevive a um crash • Sobrevive a um crash de Durabilidade de servidor servidor 43 6. JDBC (JAVA DATABASE CONNECTIVITY) JDBC é uma API desenvolvida pela Sun para acesso a SGBD (Sistemas Gerenciadores de Banco de Dados) relacionais por meios de comando SQL (Structured Query Language). Esta API é a mesma para qualquer SGBD, fazendo com que as aplicações necessitem de pouca adaptação para diferentes bancos de dados. Além disso, a API mantém a independência de plataforma da linguagem Java, rodando em qualquer sistema operacional e acessando qualquer banco de dados através do SQL. Estas características são o principal motivo da linguagem Java e do JDBC ter uma vantagem fundamental sobre outros ambientes de programação de banco de dados. Existem também drivers ODBC que possuem os mesmos propósitos do JDBC 44 rodando em várias plataformas. Porém, eles são normalmente escritos em C, o que limita a portabilidade e a auto-instalação dos programas Java, o ODBC tem que ser instalado e configurado na máquina do cliente o que pode se tornar mais trabalhosa a tarefa de uma implantação de um sistema. Entretanto, bancos de dados que utilizam ODBC podem ser utilizados em qualquer aplicação Java via a ponte JDBC-ODBC. JDBC é uma API que representa uma abordagem robusta e orientada a objetos para acesso a banco de dados relacionais. A API implementa a maioria das funcionalidades do ODBC, só que ela foi criada de uma forma consistente em contraste com a coleção de funções e estruturas complexas do ODBC. O JDBC é uma solução orientada a objeto que encapsula as funcionalidades aos banco de dados em um conjunto de classes. Estas classes são utilizadas para instanciar objetos que são então utilizadas para interagir com o banco e dados. Esta API fornece uma interação com a linguagem Java através de uma interface natural de programação. A figura abaixo representa a arquitetura JDBC: 45 Arquitetura JDBC 1 O Driver Manager é uma classe que seleciona o Driver apropriado para a conexão com o banco de dados. Ela é responsável pela interface entre a aplicação e os drivers JDBC, e também exige que os drivers JDBC sejam registrados. Também efetua a conexão com o banco de dados a partir da seleção de um driver registrado. Os Drivers podem ser registrados e carregados dinamicamente (DriverManager.registerDriver e class.forName ), as querys (consultas) e fetches ( captura do resultado da consulta ) são realizadas diretamente através do driver JDBC. 46 A estrutura de endereçamento é semelhante ao esquema de URLs (Uniform Resource Locators) por exemplo : jdbc:oracle:thin:@serverhostname:1521:ORCL Algumas classes API do JDBC são destacadas a seguir: • Java.sql.DriverManager: carrega os drivers de acesso a banco de dados e gerencia as conexões. • Java.sql.Connection: representa uma conexão com um banco de dados. • Java.sql.Statement: é responsável pela execução de sentenças SQL durante uma conexão com o banco de dados, que podem ser précompiladas ou mesmo stored procedures. • Java.sql.ResultSet: controla a manipulação dos dados resultantes de uma sentença. Abaixo estão dois exemplo de uma classe feita para conexão através do JDBC diretamente, uma executando uma query que retorna parâmetros e a outra executando um store procedure: package jdbc; 47 import java.sql.*; import java.util.*; public class Pure extends Thread { public static void main(String[] args) { try { // fazendo uma consulta simples passando parâmetros String driver = "com.inet.tds.TdsDriver"; String url = "jdbc:inetdae7:servLine2"; String user = "usr_servLine"; String pass = "usr_servLine"; String query = "select * from tb_cashStatus where CP_CE_CD_Agencia = ?"; Class.forName( driver ); Connection con = DriverManager.getConnection(url,user,pass); PreparedStatement pstm = con.prepareStatement( query ); pstm.setString(1,"15"); ResultSet rs = pstm.executeQuery(); while ( rs.next() ) { System.out.println( "ag = " + rs.getString( 1 ) ); System.out.println( "ps = " + rs.getString( 2 ) ); System.out.println( "tr = " + rs.getString( 3 ) ); } } catch (Exception ex) { ex.printStackTrace(); } } } package jdbc; 48 import java.sql.*; import java.util.*; public class StoredProcedure extends Thread { public static void main(String[] args) { try { // chamando uma stored Procedure String String String String driver = "com.inet.tds.TdsDriver"; url = "jdbc:inetdae7:servLine2"; user = "usr_servLine"; pass = "usr_servLine"; Class.forName( driver ); Connection con = DriverManager.getConnection(url,user,pass); CallableStatement cstm = con.prepareCall( "{ call SP_ALine }" ); cstm.setString(1,"254"); ResultSet rs = cstm.executeQuery(); while ( rs.next() ) { System.out.println( "ag = " + rs.getString( 1 ) ); System.out.println( "ps = " + rs.getString( 2 ) ); System.out.println( "tr = " + rs.getString( 3 ) ); } } catch (Exception ex) { ex.printStackTrace(); } } } 49 6.1 TIPOS DE DRIVERS Existem quatro tipos de Drivers: • Ponte com ODBC: Este já foi mencionado anteriormente, e utiliza uma ponte JDBC-ODBC para manter compatibilidade com os bancos de dados existentes do tipo ODBC. Ele requer que o Driver ODBC esteja disponível na máquina cliente. • Acesso ao Driver Nativo: É um driver parcialmente escrito na linguagem de programação Java e parcialmente em código nativo que se comunica com a API cliente de um banco de Dados. Quando esse driver é usado, deve-se instalar algum código específico da plataforma além de uma biblioteca Java. • Driver escrito em Java com protocolo de rede: É uma biblioteca cliente Java pura que usa um protocolo independente de banco de dados para comunicar pedidos do banco de dados para um componente servidor. A biblioteca cliente é independente do banco de dados real, simplificando assim a distribuição. 50 • Driver escrito em Java com protocolo nativo: É uma biblioteca Java pura que transforma pedidos em um protocolo específico do banco de dados, para realizar solicitações. 51 7. SOLUÇÃO PROPOSTA Foi proposto o desenvolvimento de uma Interface de acesso a banco de dados, com o objetivo de facilitar as tarefas do desenvolvedor. Utilizando esta Interface o desenvolvedor possui uma grande flexibilidade nas suas consultas, ela pode exercer um poder sobre o banco, o mesmo acontece se o acesso ao banco for diretamente através do JDBC. Porem, existe um diferencial onde o desenvolvedor não precisa conhecer o banco, já que é o nosso componente que irá interagir com a API JDBC. 52 O nossa proposta vai parcialmente de encontro com o uso de Entity bean pelo desenvolvedor já que não é necessário conhecer o banco de dados. A utilização do Entity Bean, também tem uma série de vantagens, porém como foi mencionado anteriormente, no capítulo onde se falou do Entity Bean (sessão 4.1), eles não possuem flexibilidade na manipulação dos dados, além de serem muito pesados, já que são eles que cuidam da persistência dentro do banco de dados. Note que o desenvolvimento desta Interface encapsula as vantagens que existem nos métodos atuais de acesso a banco de dados, herdando o que tem de melhor no acesso ao banco via JDBC diretamente e na utilização dos Entity bean. Primeiramente mostrarei um exemplo que ilustra o mecanismo básico para uma aplicação Java acessar um banco de dados, utilizando a API que foi proposta no nosso trabalho. 53 Aplicação Java API DE ACESSO API JDBC SGBD Funcionamento API de Acesso 1 Na figura acima, a aplicação Java corresponde a qualquer aplicativo que irá utilizar a nossa API de acesso, É na API de acesso que estão englobadas todas as características do EJB que foram citadas. É através da API JDBC que a API de acesso irá fazer a conexão com o banco de dados. O exemplo a seguir mostra mais detalhadamente o funcionamento da API de acesso. 54 Funcionamento API de Acesso 2 No passo 1, a aplicação obtém uma instância para o Session bean que por sua vez, através do JDBC no passo 2, obtém a conexão. No passo 3 o banco de dados retorna um conjunto de linhas com o resultado, no passo 4 o JDBC devolve esse resultado para a API de acesso que pega esse conjunto de linhas reescreve, armazena e devolve para a aplicação no passo 5. A API de acesso reescreve o conjunto de linhas recebidos para evitar que a aplicação faça qualquer acesso ao banco. 55 Abaixo estão dois exemplos de como uma aplicação utiliza para acessar o banco de dados através da nossa API, o exemplo está executando uma query que retorna parâmetros e o outro executando um store procedure. package monografia; import import import import javax.naming.InitialContext; javax.rmi.PortableRemoteObject; monografia.ComandoSql; monografia.ComandoSqlHome; public class TestaProcedure { public static void main(String[] args) { try { InitialContext jndiContext = new InitialContext(); // Pegando Object ref reference para o Bean ComandoSql = jndiContext.lookup("ComandoSql"); // Pegando reference para a Interface Home do Bean ComandoSqlHome home = (ComandoSqlHome) PortableRemoteObject.narrow(ref, ComandoSqlHome.class); // Criando o Objeto para o Bean ComandoSql ComandoSql comandoSql = home.create("Oracle"); Resultado r = comandoSql.executeStoredProcedure("{ call SP_Aline }"); while (r.temMaisLinhas()) { System.out.println( r.getString("nome")); } } catch(Exception e) 56 { e.printStackTrace(); } } package monografia; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import monografia.ComandoSql; import monografia.ComandoSqlHome; public class ComandoSqlClient { public static void main(String[] args) { try { // Pegando contexto InitialContext jndiContext = new InitialContext(); // Pegando Object ref reference para o Bean ComandoSql = jndiContext.lookup("ComandoSql"); // Pegando reference para a Interface Home do Bean ComandoSqlHome home = (ComandoSqlHome) PortableRemoteObject.narrow(ref, ComandoSqlHome.class); // Criando o Objeto para o Bean ComandoSql ComandoSql comandoSql = home.create("Oracle"); comandoSql.setClausula("select * from Aluno where matricula = :matricula"); comandoSql.setParamametro("matricula","5000"); Resultado r = comandoSql.executeQuery(); while (r.temMaisLinhas()) { System.out.println( r.getString("nome")); 57 } } catch(Exception e) { e.printStackTrace(); } } } 58 8. JSP ( JAVA SERVER PAGE ) Para demonstrar as classes desenvolvidas neste trabalho iremos usar o JSP que é uma tecnologia desenvolvida pela Sun baseada em servlets. Servlets são programas Java executados dentro de um servidor web, atendendo as requisições por informação na forma de páginas HTML (Hyper Text Markup Language). JSP são páginas HTML que incluem códigos Java e outras Tags específicas. Veja abaixo um exemplo de um JSP: <html> <head> <title>Exemplo JSP</title> 59 </head> <body> <% String x= “ Ola mundo !“ %> <%=x%> </body> <html> Desta maneira as partes estáticas da página não precisam ser geradas por println. Elas são fixadas na próxima página. A parte dinâmica é gerada pelo código JSP, desta maneira a parte estática pode ser gerada por pessoas que não entendem de Java. A primeira vez que uma página JSP é carregada pelo container JSP o código Java é compilado gerando um Servlet que é executado, gerando uma página HTML que é enviada para o navegador. 60 9. JBOSS Existem vários tipos de servidores EJB a venda no mercado, como o WebLogic da empresa chamada BEA ou o WebSphere da IBM. Eles não são iguais, mas seguem determinados padrões definidos pela Sun. Estes produtos custam na faixa de 50 a 300 mil reais, mas para nossa facilidade existe o JBoss que talvez não tenha recursos tão sofisticados como os citados acima mas é gratuito e pode ser baixado pela Internet. Além do custo, o JBoss é um servidor que exige pouco espaço em disco e roda muito bem em uma máquina de 64MB, apesar dele não ser um servidor web ele possui 61 pacotes que incluem Tomcat ( que é um servidor de páginas com as características de interagir com os servlets e JSP), a sua instalação e configuração é rápida e prática. No apêndice 1 podemos encontrar como é realizada a instalação do JBoss. 62 10. CONCLUSÃO É notória a evolução da informática nos últimos anos, a forma de como os sistemas são desenvolvidos sofreu uma enorme mudança, tornando muitas vezes o desenvolvimento de aplicações mais complexos para o desenvolvedor. Hoje existe uma necessidade de se trabalhar com uma arquitetura em n-camadas e para isso foi desenvolvida a tecnologia Enterprise JavaBean , porém muita coisa precisa ser feita para melhorar este trabalho, principalmente com relação ao acesso a dados. Este projeto facilita o desenvolvimento de aplicações usando EJB, construindo uma interface padronizada e fácil de ser utilizada provendo ao programador recursos na 63 implementação de suas aplicações, diminuindo o tempo e o custo das mesmas, pois o desenvolvedor se concentra nos seus objetivos específicos sem ter que ficar procurando constantemente a API do Java para encontrar os comandos para acessar o banco de dados. Foi mostrado neste trabalho a importância da tecnologia Enterprise JavaBeans, que surgiu para facilitar o desenvolvimento de sistemas distribuídos, além de ter sido explanado várias características fundamentais desta tecnologia, pois foi com base nela que desenvolvemos a nossa API de acesso. Foram vistas também as dificuldades existentes para acessar o banco de dados via JDBC e as vantagens que a nossa API fornece para os desenvolvedores. 64 11. APÊNDICE 1 – INSTALAÇÃO DO JBOSS. A instalação do servidor de aplicação JBoss é muito simples. Após obter uma cópia dos arquivos binários no site http://www.jboss.org deve-se descompactar os arquivos em um diretório. Em seguida é necessário configurar a conexão com o SGBD. Para isso teremos que copiar o driver JDBC para diretório lib\ext do diretório da distribuição do JBoss. No nosso exemplo vamos usar o banco de dados Oracle. O arquivo Jboss.jcml deve ser editado para informar alguns dados sobre o SGBD a ser utilizado. A primeira coisa a ser feita é informar o nome completo do Driver JDBC para que o mesmo possa ser carregado. Veja a seguir como fazê-lo: <mbean code="org.jboss.jdbc.JdbcProvider" name="DefaultDomain:service=JdbcProvider"> <attribute name="Drivers">org.hsql.jdbcDriver,org.enhydra.instantdb.jdbc.idb Driver</attribute> </mbean> 65 No nosso caso, como iremos usar o banco de dados Oracle, é necessário que acrescentemos o nome do driver, como é mostrado no exemplo abaixo: < mbean code="org.jboss.jdbc.JdbcProvider" name="DefaultDomain:service=JdbcProvider"> <attribute name="Drivers">oracle.jdbc.driver.OracleDriver</attribu> </mbean> Também precisamos informar a URL do SGBD, o nome do usuário e a senha que será utilizada, como mostrado no exemplo abaixo: <mbean code="org.jboss.jdbc.XADataSourceLoader" name="DefaultDomain:service=XADataSource,name=OracleDB"> <attribute name="PoolName">OracleDB</attribute> <attribute name="DataSourceClass">org.jboss.pool.jdbc.xa.wrapper.XADataSourc eImpl</attribute>[JBoss 2.4] <attribute name="URL">jdbc:oracle:thin:@serverhostname:1521:ORCL</attribute> <attribute name="JDBCUser">scott</attribute> <attribute name="Password">tiger</attribute> </mbean> Assim que iniciarmos o JBoss, saberemos que o Driver foi carregado se aparecer a seguinte mensagem na tela de output do servidor de aplicação: 66 [JDBC] Loaded JDBC-driver:oracle.jdbc.driver.OracleDriver Caso o Driver não tenha sido carregado irá aparecer a seguinte mensagem: [JDBC] Could not load driver: oracle.jdbc.driver.OracleDriver 67 12. APÊNDICE 2 – CLASSES DA API DE ACESSO /** * @author Aline Souto Bezerra * [email protected] */ package monografia; import java.io.Serializable; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; /** * Implementação da Inteface home do Session Bean * Está Interface possibilita que o cliente tenha uma referência para o bean */ public interface ComandoSqlHome extends EJBHome { public ComandoSql create(String poolName) throws RemoteException, CreateException; } 68 /** * @author Aline Souto Bezerra * [email protected] */ package monografia; import javax.ejb.EJBObject; import java.rmi.RemoteException; import java.sql.*; /** * Implementação da Interface remota * Está classe Define os métodos do bean para que a aplicação cliente * a utilize */ public interface ComandoSql extends EJBObject { public Resultado executeQuery() throws RemoteException; public void setClausula(String str); public int executeUpdate(); public void setParametro(String paramName, Object value); public int executarProcedimento() throws RemoteException; public Resultado executarFuncao() throws RemoteException; public int executeUpdateProcedure() throws RemoteException; } 69 /** * @author Aline Souto Bezerra * * [email protected] */ package monografia; import java.rmi.RemoteException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.naming.*; import javax.sql.*; import java.sql.*; import java.util.HashMap; /** * Interface do Session bean que serve para executar sentenças SQL * (consultas e atualizações) genéricas. * */ public class ComandoSqlBean implements SessionBean { protected String clausula; protected HashMap params; protected String poolName; /** * Este método cria a conexão com o banco. * @param poolName - nome do pool que é usado no banco. */ public void ejbCreate(String poolName) { params = new HashMap(); this.poolName = poolName; } /** * Este método é usado para setar os parâmetros * @param paramName - é o nome do campo da tabela * @param value valor do campo * @exception RemoteException - Exceção lançada caso o * parametro seja passado incorretamente */ public void setParametro(String paramName, Object value) throws RemoteException { paramName = paramName.toLowerCase(); if (clausula.toLowerCase().indexOf(":" + paramName) < 0) throw new IllegalArgumentException(paramName); if (value == null) 70 throw new IllegalArgumentException("Valor nulo para " + paramName); params.put(paramName, value); } private String getComandoSql(HashMap params) { String newComando = clausula; int aux = newComando.indexOf(':'); while (aux > 0) { int aux1 = aux; while (aux1 < newComando.length() && newComando.charAt(aux1) != ' ' && newComando.charAt(aux1) != ',' && newComando.charAt(aux1) != '\'' && newComando.charAt(aux1) != '%' && newComando.charAt(aux1) != ')') aux1++; String paramName = newComando.substring(aux + 1, aux1); Object paramValue = params.get(paramName.toLowerCase()); if (paramValue == null) paramValue = ":" + paramName; newComando = newComando.substring(0, aux) + paramValue.toString() + newComando.substring(aux1); aux += paramValue.toString().length(); aux = newComando.indexOf(':', aux); } return newComando; } /** * Este método obtem o Comando SQL que vai ser executado. * param str - sentença do comando que será executado. */ public void setClausula(String str) { clausula = str; } /** * Executa uma sentença SQL de consulta retornando o resultado da consulta. * @return Resultado conjunto de linhas retornadas do BD * @exception RemoteException Ocorre quando o acesso ao banco de dados * não pode ser feito ou se a senetença está incorreta. */ public Resultado executeQuery() throws RemoteException { try { Connection con = getConexao(); Resultado r = new Resultado(con.createStatement().executeQuery( getComandoSql(params) )); con.close(); return r; } catch (Exception ex) { ex.printStackTrace(); throw new RemoteException( ex.getMessage() ); 71 } } /** * Executa uma sentença SQL de insert ou update. * @return int - número de linhas afetadas no banco de dados * @exception RemoteException Ocorre quando o acesso ao banco de dados * não pode ser feito ou se a senetença está incorreta. */ public int executeUpdate() throws RemoteException { try { Connection con = getConexao(); int cont = con.createStatement().executeUpdate( clausula ); con.commit(); con.close(); return cont; } catch (Exception ex) { ex.printStackTrace(); throw new RemoteException( ex.getMessage() ); } } /** * Executa uma sentença SQL de alteração. * @return int - número de linhas afetadas no banco de dados * @exception RemoteException Ocorre quando o acesso ao banco de dados * não pode ser feito ou se a senetença está incorreta. */ public int executeUpdateProcedure() throws RemoteException { try { Connection con = getConexao(); clausula = "{ call " + clausula + " }" ; System.out.println(clausula); int cont = con.prepareCall(getComandoSql(params)).executeUpdate(); con.close(); return cont; } catch (Exception ex) { ex.printStackTrace(); throw new RemoteException( ex.getMessage() ); } } public int executarProcedimento() throws RemoteException { try { Connection con = getConexao(); clausula = "{? = call " + clausula + " }" ; CallableStatement stm = con.prepareCall(clausula); 72 stm.registerOutParameter(1,Types.NUMERIC) ; stm.executeQuery(); int i = stm.getInt(1); con.close(); return i; } catch (Exception ex) { ex.printStackTrace(); throw new RemoteException( ex.getMessage() ); } } public Resultado executarFuncao() throws RemoteException { try { Connection con = getConexao(); int i =0; String retorno = new String (" "); i = clausula.indexOf(' '); retorno = clausula.substring(0,i); int aux = clausula.length(); clausula = clausula.substring(i,aux); clausula = "{ " + retorno + " = call " + clausula + " }" Resultado r = new Resultado( con.prepareCall(getComandoSql(params)).executeQuery() ); con.close(); return r; } catch (Exception ex) { ex.printStackTrace(); throw new RemoteException( ex.getMessage() ); } } ; private Connection getConexao() throws Exception { Context ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup("java:/" + poolName); return ds.getConnection(); } public public public public void void void void ejbRemove(){} ejbActivate(){} ejbPassivate(){} setSessionContext(SessionContext sc){} } 73 /** * @author Aline Souto Bezerra * * [email protected] */ package monografia; import import import import import import import import import java.util.ArrayList; java.util.Collection; java.sql.ResultSet; java.sql.ResultSetMetaData; java.sql.Types; java.sql.Date; java.sql.Time; java.sql.Timestamp; java.rmi.RemoteException; /** * Resultado * Classe utilizada para encapsular o resultado do banco de dados, nela é * criado um array e cada índice do array aponta para um outro array, * o array de índice 0 representa o cabeçalho da tabela e os demais representam * uma linha de uma coluna. * */ public class Resultado implements java.io.Serializable { protected ArrayList linhas; protected int posicao; /** * Construtor padrão da classe Resultado. * Na construção de um objeto Resultado o ResultSet é percorrido e todos os dados são * passados para um ArrayList. * @param ResultSet - Usado para passar as linhas do resultado 74 */ public Resultado(ResultSet rs) throws RemoteException { try { // cabecalho - nome das colunas linhas = new ArrayList(); ResultSetMetaData rsmd = rs.getMetaData(); int qtdColunas = rsmd.getColumnCount(); ArrayList cabecalho = new ArrayList(); for (int i = 1; i <= qtdColunas; i++) { cabecalho.add( rsmd.getColumnName( i ).toLowerCase() ); } posicao = 0; linhas.add(cabecalho); // dados while ( rs.next() ) { ArrayList novaLinha = new ArrayList(); for (int i = 1; i <= qtdColunas; i++) { novaLinha.add( rs.getObject( i ) ); } linhas.add( novaLinha ); } } catch (Exception ex) { ex.printStackTrace(); throw new RemoteException( ex.getMessage() ); } } /** * Método usado para saber se existe mais inhas na tabela retornando um valor * falso ou verdadeiro. 75 * @return boolean - retorna verdade até existir mais linhas. */ public boolean temMaisLinhas() { return ++posicao < linhas.size(); } /** * getString - Este metodo é utilizado para se obter uma coluna do conjunto * resultado na forma de string. Independente de qual seja o tipo do dado no * SGBD ele será convertido para uma string. * @param indice - Indice usado para localizar a coluna * @return string - string representando a coluna * @exception RemoteException - Exceção lançada caso o índice esteja fora dos limites */ public String getString(int indice) throws RemoteException { validaIndice( indice ); Object str = ((ArrayList)linhas.get(posicao)).get( indice ); if( str == null ) return null; if( str instanceof String ) return ( String ) str; else return str.toString(); } /** * getInteger - Este metodo é utilizado para se obter uma coluna do conjunto * resultado na forma de Inteiro. Independente de qual seja o tipo do dado no * SGBD ele será convertido para um Inteiro. * @param indice - Indice usado para localizar a coluna * @return Inteiro - valor encontrado na coluna * @exception RemoteException - Exceção lançada caso o índice esteja fora dos limites */ public Integer getInteger(int indice) throws RemoteException 76 { validaIndice( indice ); Object value = ((ArrayList)linhas.get(posicao)).get( indice ); if( value == null ) return null; if( value instanceof Integer ) return ( Integer ) value; if( value instanceof Number ) return new Integer( ( ( Number )value ).intValue() ); else return new Integer( value.toString() ); } /** * getBoolean - Este metodo é utilizado para se obter uma coluna do conjunto * resultado na forma de Boolean. Independente de qual seja o tipo do dado no * SGBD ele será convertido para Boolean. * @param indice - Indice usado para localizar a coluna * @return Boolean - valor encontrado na coluna * @exception RemoteException - Exceção lançada caso o índice esteja fora dos limites */ public Boolean getBoolean(int indice) throws RemoteException { validaIndice( indice ); Object value = ((ArrayList)linhas.get(posicao)).get( indice ); if( value == null ) return null; if( value instanceof Boolean ) return ( Boolean ) value; if( value instanceof Number ) return new Boolean( ( ( Number )value ).intValue() != 0 ); else return new Boolean( value.toString() ); } /** * getDate - Este metodo é utilizado para se obter uma coluna do 77 conjunto * resultado na forma de Data. Independente de qual seja o tipo do dado no * SGBD ele será convertido para Data. * @param indice - Indice usado para localizar a coluna * @return Date - valor encontrado na coluna * @exception RemoteException - Exceção lançada caso o índice esteja fora dos limites */ public Date getDate(int indice) throws RemoteException { validaIndice( indice ); Object value = ((ArrayList)linhas.get(posicao)).get( indice ); if( value == null ) return null; if( value instanceof Date ) return ( Date ) value; if( value instanceof Number ) return new Date( ( ( Number )value ).longValue() ); if( value instanceof Time ) return new Date( ( ( Time )value ).getTime() ); if( value instanceof Timestamp ) return new Date( ( ( Timestamp )value ).getTime() ); else try { return new Date(java.text.DateFormat.getInstance().parse(value.toString()).g etTime()); } catch( java.text.ParseException e ) { throw new RuntimeException( "Tipo inesperado na conversão para Date: " + value.getClass().getName() ); } } /** * getString - Este metodo é utilizado para se obter o índice da coluna que se * deseja obter o conjunto do resultado, o nome da coluna é passado como * parâmetro 78 * @param nomeColuna - Nome da coluna usado para localizar o índice da coluna * que se deseja obter o conjunto de resultados * @return String - String retornando a coluna * @exception RemoteException - Exceção lançada caso o nome da coluna esteja * inválido. */ public String getString(String nomeColuna) throws RemoteException { try { return getString( ((ArrayList)linhas.get(0)).indexOf(nomeColuna.toLowerCase()) ); } catch (Exception e) { e.printStackTrace(); throw new RemoteException("Nome de coluna invalido."); } } /** * getInteger - Este metodo é utilizado para se obter o índice da coluna que se * deseja obter o conjunto do resultado, o nome da coluna é passado como * parâmetro * @param nomeColuna - Nome da coluna usado para localizar o índice da coluna * que se deseja obter o conjunto de resultados * @return Integer - valor encontrado na coluna * @exception RemoteException - Exceção lançada caso o nome da coluna esteja * inválido. */ public Integer getInteger(String nomeColuna) throws RemoteException { try { return getInteger( ((ArrayList)linhas.get(0)).indexOf(nomeColuna.toLowerCase()) ); } 79 catch (Exception e) { e.printStackTrace(); throw new RemoteException("Nome de coluna invalido."); } } /** * getBoolean - Este metodo é utilizado para se obter o índice da coluna que se * deseja obter o conjunto do resultado, o nome da coluna é passado como * parâmetro * @param nomeColuna - Nome da coluna usado para localizar o índice da coluna * que se deseja obter o conjunto de resultados * @return Boolean - valor encontrado na coluna * @exception RemoteException - Exceção lançada caso o nome da coluna esteja * inválido. */ public Boolean getBoolean(String nomeColuna) throws RemoteException { try { return getBoolean( ((ArrayList)linhas.get(0)).indexOf(nomeColuna.toLowerCase()) ); } catch (Exception e) { e.printStackTrace(); throw new RemoteException("Nome de coluna invalido."); } } /** * getDate - Este metodo é utilizado para se obter o índice da coluna que se * deseja obter o conjunto do resultado, o nome da coluna é passado como * parâmetro * @param nomeColuna - Nome da coluna usado para localizar o índice da coluna 80 * que se deseja obter o conjunto de resultados * @return Date - valor encontrado na coluna * @exception RemoteException - Exceção lançada caso o nome da coluna esteja * inválido. */ public Date getDate(String nomeColuna) throws RemoteException { try { return getDate( ((ArrayList)linhas.get(0)).indexOf(nomeColuna.toLowerCase()) ); } catch (Exception e) { e.printStackTrace(); throw new RemoteException("Nome de coluna invalido."); } } public Collection nomeDasColunas() { return (ArrayList)linhas.get(0); } public int quantidadeLinhas() { return linhas.size() - 1 ; } public int quantidadeColunas() { return ((ArrayList)linhas.get(0)).size(); } protected void validaIndice(int indice) throws RemoteException { if ( posicao < 0 || posicao >= linhas.size() ) throw new RemoteException("Linha inválida"); if ( indice < 0 || indice >= ((ArrayList)linhas.get(0)).size() ) throw new RemoteException("Indice invalido. " + indice); } } 81 13. REFERÊNCIAS BIBLIOGRÁFICAS MONSON-HAEFEL, Richard. Enterprise JavaBeansTM. 2.ed. Sebastopol, CA, EUA: O’Reilly & Associates, Inc, 2000. 471p. KASSEN- Nicolas. Designing Enterprise Applications with the JavaTM Platform, Enterprise Edition. Addison Wesley,2000.113p. SILBERSCHTZ, Abrahan; KORTH, Henry; SUDARSHAN, S. Sistemas de Banco de dados,: Makron Books, São Paulo, 1999.460p. SZYPERSKI, Clemens. Component Software :Addison-Wesley, Great Britain, 1999. Enterprise JavaBeansTM Technology. Encontrado em: http://java.sun.com/products/ejb JDBC Technology. Encontrado em: http://java.sun.com/products/jdbc/ Enterprise JavaBeans Tutorial. Encontrado em: http://developer.java.sun.com/developer/onlinetraining/beans/ejbtutorial index.html 82 JBoss 2.2+ Documentation. Encontrado em: http://www.jboss.org/online-manual/HTML/index.html Interface entre Java e Bancos de dados relacionais. Encontrado em: http://www.dca.fee.unicamp.br/courses/PooJava/javadb/jdbc.html Servlets e Enterprise Javabeans. Encontrado em: http://www.dmu.com/ejw1.html JAVA - Integrando Banco de Dados Via JDBC. Encontrado em: http://www.pr.gov.br/celepar/celepar/batebyte/edicoes/1996/bb58/java.htm 83