Entity beans BMP Persistência explícita Ricardo Cavalcanti [email protected] Jobson Ronan [email protected] Introdução Entity beans são objetos persistentes Sincronizam seu estado através um mecanismo de persistência Dois tipos de gerenciamento de persistência Bean Managed Persistence Container Managed Persistence 2 Persistência Serialização Facilidade de leitura e gravação Dificuldade de consultas à base Alternativas Bancos de dados relacionais (JDBC) Bancos de dados Orientados a objeto Mapeamento Objeto relacional 3 O/R Mapping Decompor os objetos em campos de uma tabela Classe Livro String ISBN String autor String titulo ISBN Autor Titulo 0471208310 MARINESCU, FLOYD EJB DESIGN PATTERNS 0321278658 BECK, KENT EXTREME PROGRAMMING EXPLAINED - EMBRACE CHANGE 8575220470 TELES, VINICIUS MANHAES EXTREME PROGRAMMING instância: Livro ISBN = 0321278658 Autor = “BECK, KENT” Titulo = “EXTREME PROGRAMMING...” 4 Entity bean Session beans modelam tarefas realizadas pelo sistema Entity beans modelam as entidades Representam dados Session beans stateful não representam dados! Independente da forma de armazenamento Dois termos distintos Entity bean instance: os dados na memória (instância da classe do Entity Bean) Entity bean data: os dados fisicamente armazenados no banco 5 Mais diferenças entre Session e Entity Entity beans têm ciclo de vida independente da aplicação cliente têm identidade e estado visível para o cliente são concorrentes 6 Componentes do Entity bean Além de: interface Home ou LocalHome, interface Remote ou Local, deployment descriptor, classe EJB Mapeamento da classe à uma definição de entidade de um esquema de banco de dados métodos especiais que lidam com a sincronização do EntityBean com o banco de dados Um entity bean tem uma classe Primary Key que permite identificar sua instância 7 Primary Key Identifica o entity bean unicamente Uma classe PK deve ser serializável e têm Pelo menos um construtor público sem argumentos Atributos públicos que formam a chave Métodos equals e hashcode String e Wrapper classes podem ser usados 8 Exemplo: Primary Key public class AccountPK implements java.io.Serializable { public String accountID; public AccountPK(String id) { this.accountID = id; } public AccountPK() { } public String toString() { return accountID; } public int hashCode() { return accountID.hashCode(); } public boolean equals(Object account) { return ((AccountPK) account).accountID.equals(accountID); } } 9 Características do Entity bean Tolerante a falhas Podem ser reconstituídos após a queda do servidor Basta ler os dados novamente! Ciclo de vida muuuuuito longo Antes mesmo da aplicação pensar em ser feita já existem Entity beans Dados legados 10 Sincronia A sincronia com a base permite pensar instance e data com um só. Container controla a sincronia void ejbLoad() Todas as classes implementam métodos que realizam essa atualização lê os dados da base para dentro do entity bean void ejbStore() grava os dados do entity bean na base 11 ejbLoad e ejbStore Container/Server 1:ejbLoad() 4:ejbStore() Instância de Entity bean 2:Lê da base 3: Métodos de negócio 5: grava na base Base de dados 12 Concorrência Entity beans são concorrentes Múltiplas instâncias podem representar o mesmo dado Melhorar performance O Container controla a sincronia através de transações 13 Pooling Para otimizar recursos Economizar tempo de cliente criação ejbActivate() Free Pool Adquirir recursos ejbPassivate() Largar recursos cliente 14 Pooling e sincronismo Container Instância do bean 1:ejbStore() 2:ejbPassivate() 1:ejbLoad() 2:ejbActivate() 15 Tipo de persistência Bean-managed persistence (BMP) UPDATE no ejbStore, SELECT no ejbLoad e INSERT no ejbCreate (com um BD relacional) Container-managed persistence (CMP) O container gera código para garantir a sincronia com a base 16 Criando e removendo beans Entity beans são dados, portanto Criar um bean = inserir dados na base Remover um bean = remover dados Se você tiver o seguinte ejbCreate() no bean public AccountPK ejbCreate(String id, String owner) ... Você deve ter o seguinte create() na Home public Account create(String id, String owner) ... O bean retorna a chave primária para o container, que acha o dado utilizando-a e entrega ao cliente um objeto que implementa a Component interface . Para destruir um bean: remove() na Home ou na Component interface ejbRemove() é o correspondente no bean. Ele destrói o dado, mas preserva o bean para ser reciclado 17 Localizando entity beans Entity Beans podem ser localizados quando já existirem Todo bean pode ser localizado através de sua chave primária Métodos find() adicionais podem ser criados para localizar beans de acordo com outros critérios Finder methods na home interface No home: EJBObject findXXX(...) No EJB: PK ejbFindXXX(...) Deve existir pelo menos o findByPrimaryKey() 18 Métodos find Todos devem declarar que lançam FinderException Métodos de retorno simples devem retornar uma referência de EJBObject ou EJBLocalObject, na interface Home Lançam ObjectNotFoundException, caso não encontrem o EJB Métodos com retorno múltiplo devem retornar uma Collection de chaves primárias Retornam a Collection vazia caso não encontre nada 19 Modificando sem EJB Você pode mudar diretamente na base As mudanças serão refletidas no Entity bean, porém... Problemas de cache podem ocorrer: evite! 20 Entity Context Estende EJBContext Através dele pode-se Obter o EJBObject ou o EJBLocalObject associado Obter a chave primária getPrimaryKey() é essencial em ejbLoad() e ejbRemove() para descobrir qual a instância que será carregada ou removida, respectivamente EntityContext EJBLocal Object EJBObjec t PK 21 Classe do bean Métodos da interface Home Métodos da Component interface Para cada create() da Home, um par ejbCreate/postCreate com a mesma assinatura Para cada find da remote um ejbFind correspondente Para cada “home method” xxx() um ejbHomeXxx Todos os criados por você Métodos da interface EntityBean ejbActivate e ejbPassivate ejbLoad e ejbStore ejbRemove setEntityContext e unsetEntityContext 22 Métodos da interface Home PK ejbCreate void ejbPostCreate Atua como construtor do bean, inserindo na base de dados Tipo de retorno deve ser a PK Chamado pelo container após o ejbCreate Pode ser utilizado para passar referências de outros beans para o seu bean Pode ser vazio ejbFindXXX Localiza os beans Retorna • a Component interface ou • Collection, para retornar múltiplos beans Pode haver vários, apenas o ejbFindByPrimaryKey é obrigatório ejbHomexxx Um método da interface home 23 Métodos da interface EntityBean ejbLoad ejbStore Deve conter query SELECT ou equivalente e em seguida atualizar os atributos do bean com os dados recuperados Use context.getPrimaryKey() para saber qual a chave primária do bean a ser lido Deve conter query UPDATE ou equivalente e gravar no banco o estado atual dos atributos do objeto ejbRemove Chamado antes que os dados sejam removidos do banco Deve conter query DELETE ou equivalente Use context.getPrimaryKey() para saber qual a chave primária do bean a ser removido 24 Métodos da interface EntityBean ejbPassivate ejbActivate Chamado logo após a ativação do bean setEntityContext Chamado antes da passivação do bean Chamado após a criação da instância no pool. O contexto passado deve ser gravado em variável de instância pois pode ser usado para obter a chave primária da instância atual unsetEntityContext Destrói a instância (será recolhida pelo GC). Isto destrói o objeto, mas não o entity bean (os dados) 25 Exemplo: Interface do Componente public interface Account extends EJBObject { public void deposit(double amt) throws AccountException, RemoteException; public void withdraw(double amt) throws AccountException, RemoteException; public double getBalance() throws RemoteException; public String getOwnerName() throws RemoteException; public void setOwnerName(String name) throws RemoteException; public String getAccountID() throws RemoteException; public void setAccountID(String id) throws RemoteException; } 26 Exemplo: Interface Home public interface AccountHome extends EJBHome { Account create(String accountID, String ownerName) throws CreateException, RemoteException; public Account findByPrimaryKey(AccountPK key) throws FinderException, RemoteException; public Collection findByOwnerName(String name) throws FinderException, RemoteException; public double getTotalBankValue() throws AccountException, RemoteException; } 27 Exemplo: Classe do Bean public class AccountBean implements EntityBean { protected EntityContext ctx; private String accountID; // PK private String ownerName; private double balance; // Getters e Setters public String getAccountID() {...} ... // Métodos de negócio public void deposit(double amount) {...} ... // Métodos do ciclo de vida public void ejbActivate() {...} ... // Métodos de Home public AccountPK findByPrimaryKey(AccountPK pk) {} // Métodos de persistência public void ejbLoad() {...} ... } 28 Exemplo: Acesso via DataSource private Connection getConnection() throws Exception { try { Context ctx = new InitialContext(); javax.sql.DataSource ds =(javax.sql.DataSource) ctx.lookup("java:comp/env/jdbc/ejbPool"); return ds.getConnection(); } catch (Exception e) { System.err.println("Could not locate datasource:"); e.printStackTrace(); throw e; } } 29 Exemplo: Ciclo de vida public void setEntityContext(EntityContext ctx) { System.out.println("setEntityContext called"); this.ctx = ctx; } public void unsetEntityContext() { System.out.println("unsetEntityContext called"); this.ctx = null; } public void ejbPassivate() { System.out.println("ejbPassivate () called."); } public void ejbActivate() { System.out.println("ejbActivate() called."); } 30 Exenplo: Métodos de negócio public void deposit(double amt) throws AccountException { balance += amt; } public void withdraw(double amt) throws AccountException { if (amt > balance) { throw new AccountException("Cannot withdraw "+amt+"!"); } balance -= amt; } // Getter/setter methods on Entity Bean fields public double getBalance() { return balance; } public void setOwnerName(String name) { ownerName = name; } public String getOwnerName() { return ownerName; 31 } Exemplo: ejbFindByPrimaryKey public AccountPK ejbFindByPrimaryKey(AccountPK key) throws FinderException { PreparedStatement pstmt = null; Connection conn = null; try { conn = getConnection(); pstmt = conn.prepareStatement ( "select id from accounts where id = ?"); pstmt.setString(1, key.toString()); ResultSet rs = pstmt.executeQuery(); rs.next(); return key; } catch (Exception e) { throw new FinderException(e.toString()); } finally { con.close(); ... } } 32 Exemplo: ejbCreate public AccountPK ejbCreate(String accountID, String ownerName) throws CreateException { PreparedStatement pstmt = null; Connection conn = null; try { this.accountID = accountID; this.ownerName = ownerName; this.balance = 0; conn = getConnection(); pstmt = conn.prepareStatement("insert into accounts "+ "(id, ownerName, balance) values (?, ?, ?)"); pstmt.setString(1, accountID); pstmt.setString(2, ownerName); pstmt.setDouble(3, balance); pstmt.executeUpdate(); return new AccountPK(accountID); } catch (Exception e) { throw new CreateException(e.toString()); } finally { con.close(); ... } } public void ejbPostCreate(String accountID, String ownerName) { 33 } Exemplo: ejbFindByOwnerName public Collection ejbFindByOwnerName(String name) throws FinderException { PreparedStatement pstmt = null; Connection conn = null; Vector v = new Vector(); try { conn = getConnection(); pstmt = conn.prepareStatement ("select id from accounts where ownerName = ?"); pstmt.setString(1, name); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { String id = rs.getString("id"); v.addElement(new AccountPK(id)); } return v; } catch (Exception e) { throw new FinderException(e.toString()); } finally { con.close(); ... } } 34 Exemplo: ejbHomeGetTotalBankValue public double ejbHomeGetTotalBankValue() throws AccountException{ PreparedStatement pstmt = null; Connection conn = null; try { conn = getConnection(); pstmt = conn.prepareStatement ("select sum(balance) as total from accounts"); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return rs.getDouble("total"); } } catch (Exception e) { e.printStackTrace(); throw new AccountException(e); } finally {...} throw new AccountException("Error!"); } 35 Exemplo: ejbRemove public void ejbRemove() throws RemoveException AccountPK pk = (AccountPK) ctx.getPrimaryKey(); String id = pk.accountID; PreparedStatement pstmt = null; Connection conn = null; try { conn = getConnection(); pstmt = conn.prepareStatement ("delete from accounts where id = ?"); pstmt.setString(1, id); if (pstmt.executeUpdate() == 0) { throw new RemoveException("..."); } } catch (Exception ex) { throw new EJBException("...", ex); } finally {...conn.close() ... } } 36 Exemplo: ejbStore public void ejbStore() { PreparedStatement pstmt = null; Connection conn = null; try { conn = getConnection(); pstmt = conn.prepareStatement ("update accounts set ownerName = ?, balance = ? where id = ?"); pstmt.setString(1, ownerName); pstmt.setDouble(2, balance); pstmt.setString(3, accountID); pstmt.executeUpdate(); } catch (Exception ex) { Sempre lance EJBException throw new EJBException("...", ex); quando algo sair errado em ejbStore() e ejbLoad() } finally {...conn.close() ... } } 37 Exemplo: ejbLoad public void ejbLoad() { AccountPK pk = (AccountPK) ctx.getPrimaryKey(); accountID = pk.accountID; PreparedStatement pstmt = null; Connection conn = null; try { conn = getConnection(); pstmt = conn.prepareStatement ("select ownerName, balance from accounts where id = ?"); pstmt.setString(1, accountID ); ResultSet rs = pstmt.executeQuery(); rs.next(); ownerName = rs.getString("ownerName"); balance = rs.getDouble("balance"); } catch (Exception ex) { throw new EJBException("...", ex); } finally {...conn.close() ... } } 38 Exemplo: ejb-jar.xml <ejb-jar> <enterprise-beans> <entity> <ejb-name>Account</ejb-name> <home>examples.AccountHome</home> <remote>examples.Account</remote> <local-home>examples.AccountLocalHome</local-home> <local>examples.AccountLocal</local> <ejb-class>examples.AccountBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>examples.AccountPK</prim-key-class> <reentrant>False</reentrant> <resource-ref> <res-ref-name>jdbc/ejbPool</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> </entity> </enterprise-beans> <assembly-descriptor> <container-transaction> <method> <ejb-name>Account</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> 39 Exemplo: jboss.xml <jboss> <enterprise-beans> <entity> <ejb-name>Account</ejb-name> <jndi-name>account/AccountHome</jndi-name> </entity> </enterprise-beans> <resource-managers> <resource-manager> <res-name>jdbc/ejbPool</res-name> <res-jndi-name>java:/DefaultDS</res-jndi-name> </resource-manager> </resource-managers> </jboss> 40 Patterns e Entity beans DAO Abstrai e encapsula o acesso a fontes de dados Transfer Object Antes chamado de Value Object Encapsula dados de negócio 41 DAO – Data Access Object Contexto acesso a dados persistentes. Problema acoplamento entre lógica de negócios e lógica de acesso a dados Código SQL junto do código de negócios 42 DAO – Data access object // all imports required // exceptions not handled in the sample code public class UserAccountEJB implements EntityBean { // All EJB methods like ejbCreate, ejbRemove go here // Business methods start here public UserDetails getUserDetails(String userId) { // A simple query for this example String query = "SELECT id, name, phone FROM userdetails WHERE name = " + userId; InitialContext ic = new InitialContext(); datasource = (DataSource)ic.lookup("java:comp/env/jdbc/DataSource"); Connection dbConnection = datasource.getConnection(); Statement stmt = dbConnection.createStatement(); ResultSet result = stmt.executeQuery(queryStr); // other processing like creation of UserDetails object result.close(); stmt.close(); dbConnection.close(); return(details); } } 43 DAO – Data access object Forças Componentes necessitam obter dados de uma fonte persistente A portabilidade dos componentes é afetada quando um mecanismo específico de persistência é incluído nele Os componentes devem ser transparentes quanto à persistência para prover uma migração simples para outro tipo de armazenamento 44 DAO – Data access object Solução Data access object para abstrair e encapsular todo o acesso a dados. O DAO gerencia a conexão com a fonte de dados além de armazenar e obter os dados Fonte:Core J2EE Patterns 45 DAO – Data access object 46 Fonte:Core J2EE Patterns DAO – Data Access object Pode ser associado com AbstractFactory DAOFactory Conceqüências Transparêcnia Migração simples Reduz complexidade dos objetos de negócio Centraliza acesso a dados numa nova camada Não é interessante com CMP Cria uma nova camada Quando usado com AbstractFactoru exige mais design • Pode-se evoluir de um factory method para uma abstract factory 47 Transfer Object Contexto Problema Clientes necessitam trocar dados com enterprise beans Muitas chamadas de métodos remotos para conseguir todos os atributos Forças Quantidade de chamadas remotas pode ter impacto na performance da rede Geralmente o cliente necessita de mais de um atributo Em geral, o cliente lê mais do que altera48os dados Transfer Object Solução Use um Transfer Object para encapsular os dados de negócio. Apenas um método para obter todo o objeto. O bean preenche o objeto com os atributos e envia Fonte:Core J2EE Patterns 49 Transfer Object 50 Fonte:Core J2EE Patterns Transfer Object Conseqüências Reduz o tráfico da rede Simplifica o objeto remoto e a component interface Transfere mais dados em menos chamadas Reduz duplicação de código Cópia do cliente pode ficar desatualizada Aumenta complexidade de sincronização • Objeto alterado enviado de volta ao servidor 51 Quando usar BMP Quando você precisa de controle sobre o esquema, ou precisa trabalhar com uma base legado específico. Quando a aplicação necessita de “sintonia fina” Quando portabilidade não é um problema. Mesmo nesse caso, recomenda-se utilizar DAOs. DAOs facilitam uma futura adaptação do bean para um novo esquema Quando sua consulta excede a capacidade da EJB-EL Quando sua base de dados não é suportada pelo CMP. 52