Enterprise Java Beans (IV)
Professor: Diego Passos UFF
[email protected]
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 Descrição
@Entity Indica que a classe define uma entidade
@Table(name=”<nome>”) Indica o nome da tabela a qual a classe será mapeada
@Id Atributo que é a chave primária da tabela
@GeneratedValue 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 N:N
@JoinColumn(name=”<nome>”) Indica um nome para a chave estrangeira
@DiscriminatorColumn Utilizado para identificar tuplas numa tabela resultante da união de 2 ou + classes
@DiscriminatorValue 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 Descrição
PERSIST A entidade relacionada será persistida automaticamente REMOVE A entidade relacionada será removida automaticamente MERGE A entidade relacionada será atualizada automaticamente REFRESH A entidade relacionada será trazida do banco
automaticamente
ALL 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 Descrição
@EJB Injeta stubs de beans.
@Resource Injeta recursos como filas JMS, datasources, contextos, etc.
@PersistenceContext 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
JPQL – Exemplos
• Consultas com coleções:
– 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/session- configuration.html#configuration-optional”
EJB – Entidades – Ciclo de Vida
• Similar aos métodos callback do ciclo de vida dos beans de sessão;
Anotação Descrição
@PrePersist O método é chamado imediatamente antes da entidade ser criada no banco
@PostPersist Idem para logo após
@PreRemove O método é chamado imediatamente antes da entidade ser removida do banco
@PostRemove Idem para logo após
@PreUpdate O método é chamado imediatamente antes da entidade ser atualizada no banco
@PostUpdate Idem para logo após
@PostLoad 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.
Transações
Valores para
TransactionAttributeType
Descrição
REQUIRED (default para métodos EJB)
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.
MANDATORY O chamador deve estar executando dentro de uma transação. Caso contrário, uma exceção é disparada.
REQUIRESNEW O método chamado é necessariamente executado dentro de uma nova transação. Se o chamador estiver
executando dentro de uma transação, esta é suspensa.
SUPPORTS Se o método é ativado de dentro de uma transação, esta é usada. Se não, nenhuma transação será criada.
NEVER Se o chamador está numa transação, uma exceção é disparada. Se não estiver, nenhuma é iniciada.
NOT_SUPPORTED 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 ();
} }