Enterprise Java Beans (IV) Professor: Diego Passos UFF dpassos@ic.uff.br Baseado no material original cedido pelo Professor Carlos Bazilio
Última Aula Anotações. E como são usadas para implementar EJBs. Especificidades das beans de sessão. Beans stateless. Beans statefull. Gerenciamento (pelo container). Suporte transacional.
Nesta Aula EJB orientadas a mensagens. EJB de entidade (ou JPA). Transações.
EJB Beans Orientados por Mensagens Beans que tratam mensagens recebidas via JMS (Java Messaging Service); Funcionam como listeners de filas de mensagens no contêiner EJB; Ou seja, permitem a integração com diversas aplicações via esta API de troca de mensagens; Um bean orientado por mensagem é definido através da seguinte declaração: Uma classe que implementa a interface javax.jms.messagelistener e declara a seguinte anotação: @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyvalue="javax.jms.queue"), @ActivationConfigProperty(propertyName="destination", propertyvalue="queue/<nome_da_fila>") })
EJB Beans Orientados por Mensagens No Jboss, as filas podem ser consultadas através do console de gerenciamento web. Pode-se criar novas filas no container. Filas recebem nomes no serviço JNDI. Podem ser acessadas pelos clientes pelo JNDI. Beans orientados por mensagens não guardam estado interno. Ou não deveriam. Similar aos beans de sessão stateless.
EJB Beans Orientados por Mensagens Dentro todos os tipos de EJB, as beans orientadas por mensagens têm a implementação mais simples. Não requerem codificação de interfaces cliente. Apenas requerem a implementação da classe da bean em si. Internamente, uma bean orientada por mensagem precisa ter um único método: onmessage. Método proposto na interface MessageListener. Chamado pelo container para cada mensagem recebida na fila da EJB. Método que trata as mensagens recebidas. Pode chamar métodos auxiliares.
EJB Beans Orientados por Mensagens Bean Exemplo import javax.ejb.*;import javax.jms.*; @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyvalue="javax.jms.queue"), @ActivationConfigProperty(propertyName="destination", propertyvalue="queue/mdb") }) public class CalculadoraBeanMDB implements MessageListener { public void onmessage (Message msg) { try { TextMessage t = (TextMessage) msg; System.out.println(t); } catch (Exception e) { e.printstacktrace (); }
EJB Beans Orientados por Mensagens Cliente Exemplo <% try { InitialContext ctx = new InitialContext(); Queue queue = (Queue) ctx.lookup("queue/mdb"); QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("connectionfactory"); QueueConnection cnn = factory.createqueueconnection(); QueueSession sess = cnn.createqueuesession(false,queuesession.auto_acknowledge); TextMessage msg = sess.createtextmessage( Mensagem postada! ); QueueSender sender = sess.createsender(queue); sender.send(msg); sess.close (); } catch (Exception e) {e.printstacktrace ();} %>
EJB Beans de Entidade Utilizados para modelar e acessar tabelas em bancos de dados relacionais; Usualmente, dados manipulados por uma aplicação em memória são objetos, enquanto que, quando persistidos, são representados relacionalmente. É objetivo dos beans de entidade prover facilidade neste mapeamento. Entretanto, muitos sistemas optavam por frameworks alternativos para o mapeamento Objeto-Relacional (Hibernate e Toplink Essentials) ao invés do EJB 2.1; Com isso, a Sun, criou uma especificação chamada Java Persistence, que possui API e SPI (Service Provider Interface) que permitem a utilização de diferentes frameworks. No contexto do JPA, os frameworks são chamados de providers.
EJB Java Persistence: Conceitos Básicos A unidade do Java Persistence é uma Entidade; Entidades são classes comuns (POJO s), que são anotadas de forma a indicar para o provider como se dá o mapeamento de objetos Java em tabelas; Ou seja, é o provider que gerencia uma Entidade; Entidades geralmente representam tabelas de uma base de dados relacional. Uma instância de uma entidade representa uma linha específica dentro da tabela. Um subconjunto dos atributos da entidade tipicamente representa colunas da tabela. Devem ser privados (ou protegidos).
EJB Java Persistence: Conceitos Básicos Os atributos de uma entidade que são mapeados para colunas da base de dados são chamados de atributos persistentes. Há restrições em relação aos tipos destes atributos. Podem ser: String. Tipos serializáveis de Java: BigInteger, BigDecimal, Date... Tipos serializáveis definidos pelo usuário. Arrays de tipos primitivos: byte[], char[]. Tipos enumeráveis. Outras entidades. Toda entidade deve ter uma chave primária. Atributo (ou conjunto de atributos) que identifica unicamente uma instância da entidade. Há restrições mais fortes em relação aos tipos: tipicamente, string ou inteiro.
EJB Java Persistence: Conceitos Básicos Entidades podem se relacionar com outras entidades. Exemplo: entidades Cliente e Produto. Relacionamentos têm cardinalidades: Um para um. Um para muitos. Muitos para um. Muitos para muitos. No JPA, estes relacionamentos correspondem a uma entidade ter em seus atributos uma coleção (List, tipicamente) de instâncias de outra entidade.
EJB Entidades Uma entidade é definida através da seguinte declaração: Uma classe que implementa a interface Serializable; Esta classe é preenchida com as seguintes anotações: Anotação @Entity Descrição Indica que a classe define uma entidade @Table(name= <nome> ) Indica o nome da tabela a qual a classe será mapeada @Id @GeneratedValue Atributo que é a chave primária da tabela A chave primária será automaticamente gerada pelo contêiner
EJB Entidades (... continuação de declaração de entidades): Anotações para relações: Anotação Descrição @ManyToOne N:1 @OneToMany 1:N @OneToOne 1:1 @ManyToMany @JoinColumn(name= <nome> ) N:N Indica um nome para a chave estrangeira @DiscriminatorColumn @DiscriminatorValue Utilizado para identificar tuplas numa tabela resultante da união de 2 ou + classes Valores que distinguem instâncias de classes unidas
EJB Entidades Tipos de Relacionamentos Relacionamentos bidirecionais entre objetos permitem que um objeto conheça o outro; Para tal, cada entidade precisa definir uma referência para sua parceira, a qual deve ser anotada com a cardinalidade correspondente; Além disso, na entidade que é possuída no relacionamento, a anotação deve conter o atributo mappedby, contendo o valor da propriedade correspondente que possui o relacionamento; @<AnotacaoCardinalidade>(mappedBy= <NomePropriedade> ) Neste tipo, propriedades de entidades relacionadas com cardinalidade 1:1 e N:N contém anotações OneToOne e ManyToMany em ambas as entidades; Para cardinalidade 1:N, temos a anotação OneToMany num lado e ManyToOne noutro.
EJB Entidades Tipos de Relacionamentos Se apenas uma entidade é anotada num relacionamento, temos um relacionamento unidirecional; Estes tipos de relacionamentos indicarão as possibilidades de navegação na definição de consultas.
EJB Entidades Propagação das Operações As operações sobre relações são propagadas através do atributo cascade: Sintaxe @<AnotacaoCardinalidade>(cascade=CascadeType.<TipoCascadeType>) Valores para TipoCascadeType: CascadeType PERSIST REMOVE MERGE REFRESH ALL Descrição A entidade relacionada será persistida automaticamente A entidade relacionada será removida automaticamente A entidade relacionada será atualizada automaticamente A entidade relacionada será trazida do banco automaticamente Todas as opções acima combinadas
EJB Entidades Diagrama Exemplo Relacionamento entre classes para o exemplo; As classes são definidas no padrão JavaBean (métodos get e set); Fund são os fundos de investimentos disponíveis; Investor são os perfis dos investidores; Record registra os cálculos realizados; TimedRecord inclui um timestamp nos registros.
EJB Entidades Entidade Exemplo import javax.persistence.*; import java.io.serializable; @Entity public class Fund implements Serializable { private int id; private String name; private double growthrate; public Fund () { } public Fund (String name, double growthrate) { this.name = name; this.growthrate = growthrate; } @Id @GeneratedValue public int getid () { return id; }... }
EJB Entidades Exemplo Existem diversas formas de se mapear o relacionamento de hierarquia; Por exemplo, pode-se mapear uma tabela para cada classe e utilizar chaves estrangeiras para restringir o relacionamento; A estratégia padrão de mapeamento em EJB 3 é o mapeamento da hierarquia numa única tabela; Colunas específicas são utilizadas para identificar a qual subclasse uma determinada tupla pertence; Esta coluna precisa ser definida na classe base;
EJB Entidades Entidade Exemplo import javax.persistence.*; import java.io.serializable; @Entity @DiscriminatorColumn(name="record_type") @DiscriminatorValue(value= R") public class Record implements Serializable { protected int id; protected Fund fund; protected Investor investor; protected double saving; protected double result; @ManyToOne(optional=false) @JoinColumn(name="my_fundid") public Fund getfund () { return fund; }...
EJB Entidades Entidade Exemplo import javax.persistence.*; import java.sql.timestamp; import java.io.serializable; @Entity @DiscriminatorValue (value="t") public class TimedRecord extends Record implements Serializable { private Timestamp ts;... public Timestamp getts () { return ts; } public void setts (Timestamp ts) { this.ts = ts; } }
EJB Entidades EntityManager A API que declara as funções de mapeamento de objetos em tabelas relacionais está representada pela classe EntityManager; Alguns métodos que podem ser acionados à partir de uma instância de EntityManager: persist(object): Insere o dado objeto na tabela correspondente; Object find (Class, Object): Obtém uma instância de um indivíduo da classe Class, com chave primária igual a Object; createquery(string): Retorna o resultado de uma consulta SQL (String), que pode ser uma única entidade ou uma lista destas; remove(object): Remove um dado objeto da respectiva tabela; Referência: http://java.sun.com/javaee/5/docs/api/javax/persistence/entitymanager.ht ml
EJB Entidades EntityManager Após uma entidade ser obtida, qualquer chamada de um método setter atualiza automaticamente sua respectiva tabela; Naturalmente, se obtivéssemos uma coleção de entidades num loop e atualizássemos algum atributo, estes acessos provavelmente sobrecarregariam o banco de dados; Por default, operações de criação, atualização e remoção de entidades são armazenadas numa cache; Estas são enviadas ao banco em lote quando uma das situações ocorre: a thread corrente termina ou desativação do método na pilha; uma nova consulta ao banco de dados é acionada, ou; o usuário deseja manualmente esvaziar a cache (questões de otimização, por exemplo) através da chamada do método flush() do EntityManager.
EJB Injeção de Objetos EJB 3 permite a injeção de objetos através do uso de anotações; Este recurso simplifica o trabalho do programador, eliminando a necessidade de se buscar objetos através do método lookup() de JNDI; Exemplos: Anotação @EJB @Resource @PersistenceContext Descrição Injeta stubs de beans. Injeta recursos como filas JMS, datasources, contextos, etc. Permite a injeção de um EntityManager para persistência. Estas dependências são resolvidas em tempo de execução pelo contêiner EJB.
EJB Modificando tabelas com EntityManager @Stateless public class EntityCalculator implements Calculator { @PersistenceContext protected EntityManager em; public void addfund (String name, double growthrate) { Fund fund = new Fund (name, growthrate); em.persist (fund); } public void removefund (String name) { Fund f = (Fund) em.createquery("select f from Fund f where f.name = '" + name + "'").getsingleresult(); if (f!= null) em.remove(f); } public Collection<Fund> getfunds () { return em.createquery("select f from Fund f").getresultlist(); }... }
EJB Manipulando tabelas com o EntityManager public class EntityCalculator implements Calculator {... public double calculate (int fundid, int investorid, double saving) { Investor investor = em.find(investor.class, Integer.valueOf(investorId)); Fund fund = em.find(fund.class, Integer.valueOf(fundId)); int start = investor.getstartage(); int end = investor.getendage(); double growthrate = fund.getgrowthrate(); double tmp = Math.pow(1. + growthrate / 12., 12. * (end - start) + 1); double result = saving * 12. * (tmp - 1) / growthrate; Timestamp ts = new Timestamp (System.currentTimeMillis()); TimedRecord rec = new TimedRecord (fund, investor, saving, result, ts); em.persist (rec); return result; }... }
EJB JPQL Linguagem derivada de SQL para consulta de entidades; Foi utilizada nas chamadas ao método createquery() dos slides anteriores; Com este método podemos ter acesso a um stub de uma entidade, ou a um conjunto destes; Algumas possibilidades: public Collection <TimedRecord> getrecords () { return em.createquery("select r from TimedRecord r order by r.ts desc").getresultlist(); } public Collection<TimedRecord> filterrecords(double low, double high) { return em.createquery( "from TimedRecord r where r.result > :low AND r.result < :high").setparameter ("low", new Double (low)).setparameter ("high", new Double (high)).getresultlist(); } Atualização de tabelas realizada através de chamadas a métodos set.
JPQL Exemplos Além de consultas simples a entidades, também podemos fazer consultas a relacionamentos; Expressões análogas: SELECT DISTINCT p FROM Player p JOIN p.teams t SELECT DISTINCT p FROM Player p, IN(p.teams) t SELECT DISTINCT p FROM Player p WHERE p.teams IS NOT EMPTY Selecionar jogadores distintos associados a algum time Expressões com filtro de valor: SELECT DISTINCT p FROM Player p JOIN p.teams t WHERE t.name = 'Flamengo' Condicionais: SELECT p FROM Player p WHERE p.name LIKE 'Obi%' SELECT t FROM Team t WHERE t.league IS NULL SELECT DISTINCT p FROM Player p WHERE p.salary BETWEEN :lowersalary AND :highersalary Ordem SELECT p FROM Player p ORDER BY p.teams.size DESC
Consultas com coleções: JPQL Exemplos SELECT o FROM Order o WHERE o.lineitems IS EMPTY SELECT o FROM Order o WHERE :lineitem MEMBER OF o.lineitems Consultas com sub-consultas aninhadas: SELECT c FROM Customer c WHERE (SELECT COUNT(o) FROM c.orders o) > 10 SELECT DISTINCT emp FROM Employee emp WHERE EXISTS (SELECT spouseemp FROM Employee spouseemp WHERE spouseemp = emp.spouse) SELECT emp FROM Employee emp WHERE emp.salary > ALL (SELECT m.salary FROM Manager m WHERE m.department = emp.department)
EJB JPQL Estado de uma Entidade Uma entidade pode ter 2 estados: Conectada (Managed): obtida após a chamada a um método find ou persist; Desconectada (Detached): alcança esse estado se, por exemplo, retornamos esta entidade para o cliente; Uma entidade desconectada volta ao estado conectada caso executemos os métodos persist ou merge; Além destes, outros métodos comuns são remove, flush e refresh. Referência: http://java.sun.com/javaee/5/docs/api/javax/persistence/entitymanager.ht ml
EJB Entidades Deploy Na implantação de entidades através de um arquivo JAR, o diretório META- INF precisa ter o arquivo de configuração persistence.xml ; Cada arquivo de configuração tem o seguinte formato: <persistence> <persistence-unit name="ejbpackage"> <jta-data-source>java:/defaultds</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> </persistence-unit>... </persistence> Lista de propriedades de configuração opcionais do Hibernate: http://www.hibernate.org/hib_docs/v3/reference/en/html/sessionconfiguration.html#configuration-optional
EJB Entidades Ciclo de Vida Similar aos métodos callback do ciclo de vida dos beans de sessão; Anotação @PrePersist @PostPersist @PreRemove @PostRemove @PreUpdate @PostUpdate @PostLoad Descrição O método é chamado imediatamente antes da entidade ser criada no banco Idem para logo após O método é chamado imediatamente antes da entidade ser removida do banco Idem para logo após O método é chamado imediatamente antes da entidade ser atualizada no banco Idem para logo após O método é chamado logo após um dado ser carregado do banco e associado à uma entidade
Transações Crucial no desenvolvimento corporativo, pois garante a integridade do sistema em situações excepcionais; Têm grande importância em aplicativos que acessam bancos de dados: Atualizações sucessivas usualmente necessitam que todo o conjunto tenha êxito; begin transaction verificar saldo conta débito debitar conta débito creditar conta crédito atualizar histórico movimentações commit transaction
Transações Algumas possibilidades: JTA (Java Transaction API javax.transaction) JDBC EJB Em EJB 3, transações podem ser definidas através de anotações em métodos de classes POJO: @TransactionAttribute(TransactionAttributeType); Uma transação falha quando uma aplicação dispara uma RuntimeException, típica de operações com banco de dados, ou uma exceção do usuário definida por uma classe anotada: @ApplicationException(rollback=true); Para garantir a integridade do banco de dados, o EntityManager deve executar num contexto transacional; Uso de transações implica em overhead computacional.
Valores para TransactionAttributeType REQUIRED (default para métodos EJB) MANDATORY REQUIRESNEW SUPPORTS NEVER NOT_SUPPORTED Transações Descrição O método anotado é executado dentro de uma transação. Se o método chamador estiver dentro de uma transação, esta é usada. Se não, uma nova é gerada. O chamador deve estar executando dentro de uma transação. Caso contrário, uma exceção é disparada. O método chamado é necessariamente executado dentro de uma nova transação. Se o chamador estiver executando dentro de uma transação, esta é suspensa. Se o método é ativado de dentro de uma transação, esta é usada. Se não, nenhuma transação será criada. Se o chamador está numa transação, uma exceção é disparada. Se não estiver, nenhuma é iniciada. Se o método é ativado de dentro de uma transação, esta é suspensa. Se não, nenhuma é iniciada.
Transações Exemplo import javax.ejb.applicationexception; import javax.ejb.*; @ApplicationException(rollback=true) public class TransException extends Exception { public TransException () { } }
Transações Exemplo import javax.ejb.*; import javax.persistence.*; import javax.annotation.resource; @Stateless public class TransCalculator implements Calculator { @PersistenceContext protected EntityManager em; @TransactionAttribute(TransactionAttributeType.REQUIRED) public void updateexchangerate (double newrate) throws Exception { Collection <TimedRecord> rc = em.createquery("select r from TimedRecord r").getresultlist();... throw new TransException (); } }