1. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
===============================================================================
Resumo Anotações para Certificação SCBCD 5.0
Autor:
Gilberto Augusto Holms
@gibaholms
gibaholms85@gmail.com
http://gibaholms.wordpress.com/
===============================================================================
- Introdução
. EJB: Modelo de componentes padrão do lado do servidor para aplicativos
de negócio distribuídos
- Escalonáveis
- Transacionais
- Seguros com multi-usuários
. Bean de Entidade (JPA):
- A JPA pode funcionar sozinha, porém o container EJB3 fornece
integração adicional.
- É um POJO, e não um componente (como era no 2.1).
- Obrigatório uma chave primária ( @Id ), que pode ser um
primitivo ou uma classe.
- As anotations podem estar tanto nos atributos quanto nos
métodos GETTER.
. Persistence.xml
- É obrigatório
- Sobrescreve as annotations
- Deployado num JAR junto com as entidades
- Pode declarar várias Persistence Units
- Persistence Unit -> composta por Entidades, representa uma
base de dados
. Interfaces de Negocio -> obrigatório ao menos uma interface de negócio
- javax.ejb.Local (não utiliza protocolos distribuídos para
acessar – apenas mesma JVM)
- javax.ejb.Remote
- javax.jms.MessageListener
- javax.jws.WebService
. Container EJB, seis serviços primários:
- Concorrência
- Transação
- Persistência
- Distribuição de Objetos
- Atribuição de Nomes (JNDI)
- Segurança
- Mensageria Assíncrona
- Temporização
. Serviços que melhoram o desempenho para grande número de clientes e
beans instanciados:
- Pool de instâncias (Beans Stateless e MDB):
. Clientes nunca acessam um bean diretamente, portanto o
container pode criar um pool de instancias e
gerenciá-lo reaproveitando recursos. Uma mesma instância
serve vários clientes (um de cada vez).
- Ativação (Beans Statefull)
. As variáveis de instância do bean são persistidas em
um armazenamento secundário, e o bean é removido
da memória (apassivação). Quando é invocado outro
método, uma nova instância é criada e seu estado anterior
é recuperado do armazenamento (ativação). Porém, o Stub
não perde a conexão durante a apassivação.
Página 1
2. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
. O objeto não precisa ser serializável, o fornecedor
escolhe um método de ativação que independa disso.
Para atributos transientes, o comportamento depende da
implementação do container.
. É fornecido o callback @PrePassivate e @PostActivate.
Serviços de contexto e o EntityManager devem
ser mantidos durante o processo de apassivação.
. Java EE Connectors (JCA)
. Fornece mecanismo para criação de interfaces entre o container
e sistemas legados, permitindo que os
legados disparem mensagens para MDBs do conteiner.
. Concorrência:
. Beans se sessão não executam em multithread, portanto é
proibido utilizar a palavra synchronized e também é
proibido criar threads dentro de beans, pois o container precisa
ter certeza que está com o controle sobre o
comportamento de todas as instancias do bean.
. Beans de entidade podem ser acessados de forma concorrente, o
container pode utilizar Optimistic Locking ou
isolamento Serialized no JDBC.
. Beans de mensagem podem processar mensagens simultaneamente,
onde cada instancia do pool pode processar
uma mensagem.
- Session Beans
. Interface Remota
- Todos os parametros são copiados, inclusive os objetos, mesmo
se estiver na mesma JVM (chamada "por valor")
@Remote
public interface MyBusinessInterfaceRemote { ... }
. Interface Local
- Precisa estar na mesma JVM
- Os parametros possuem comportamento normal, onde objetos são
passados por referência (chamada "por referência)
@Local
public interface MyBusinessInterfaceLocal { ... }
. Obs.:
- Parametros de tipo objeto precisam extender Serializable ou
Externalizable para poderem ser transferidos
pela rede
- Podem lançar exceções. Deve-se lançar apenas exceções de
negócio, e não de subsistemas Java
- Uma exceção de aplicativo é propagada ao cliente, e qualquer
variável de instância dela deve ser serializavel
- Todas exceções de runtime são capturadas pelo container e
empacotadas em EJBException
- Pode declarar @Local(MyBusinessInterfaceLocal.class) no
próprio bean, não precisando ele declarar a
implementação da interface explicitamente e nem precisando
anotar a interface. Idem para @Remote
. Descritor de Implantação
- Fica dentro do JAR do projeto EJB, em: META-INF/ejb-jar.xml
. Exemplo de EJB via XML
<ejb-jar>
<enterprise-beans>
Página 2
3. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
<session>
<ejb-name>ProcessPaymentBean</ejb-name>
<remote>com.titan.processpayment.ProcessPaymentRemote</remote>
<local>com.titan.processpayment.ProcessPaymentLocal</local>
<ejb-class>com.titan.processpayment.ProcessPaymentBean</ejb-class>
<session-type>Stateless</session-type>
</session>
</enterprise-beans>
</ejb-jar>
- SessionContext
public interface javax.ejb.SessionContext extends javax.ejb.EJBContext {
MessageContext getMessageContext( ) throws
IllegalStateException;
<T> getBusinessObject(Class<T> businessInterface) throws
IllegalStateException;
Class getInvokedBusinessInterface( );
//METODOS OBSOLETOS - LANÇAM EXCEPTION SE CHAMADOS
EJBLocalObject getEJBLocalObject( ) throws IllegalStateException
EJBObject getEJBObject( ) throws IllegalStateException;
}
. getBusinessObject
Retorna uma referência (do tipo da interface de
parametro) ao objeto EJB atual. É ilegal que um bean
envie uma referencia this para outro bean, para isso
serve este método
. getInvokedBusinessInterface
Retorna a classe da interface de negócio invocada para o
bean atual
. Obs.:
- Herda mais todos os métodos de EJBContext
- O container injeta o SessionContext via anotação
@Resource
- EJBContext
public interface EJBContext {
public Object lookup(String name);
// security methods
public java.security.Principal getCallerPrincipal( );
public boolean isCallerInRole(java.lang.String roleName);
// transaction methods
public javax.transaction.UserTransaction getUserTransaction( )
throws java.lang.IllegalStateException;
public boolean getRollbackOnly( ) throws
java.lang.IllegalStateException;
public void setRollbackOnly( ) throws
java.lang.IllegalStateException;
// OBSOLETO
public TimerService getTimerService( ) throws
java.lang.IllegalStateException;
// METODOS OBSOLETOS - LANÇAM RuntimeException SE CHAMADOS
public java.security.Identity getCallerIdentity( );
public boolean isCallerInRole(java.security.Identity role);
public java.util.Properties getEnvironment( );
Página 3
4. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
public EJBHome getEJBHome( ) java.lang.IllegalStateException;
public EJBLocalHome getEJBLocalHome( )
java.lang.IllegalStateException;
public Properties getEnvironment();
}
. getCallerPrincipal
Retorna o objeto Principal referente a quem está
acessando o bean
. isCallerInRole
Retorna se o usuário atual pertence a determinada role
. lookup
Permite pesquisar entradas no ENC do EJB.
--------------------------------------------------------------------------------
------------------------------------------
- Stateless Session Beans
@Stateless
public class MyBean implements MyBusinessInterface {...}
. Ciclo de Vida (TODO)
. Precisa de um construtor padrão
. Métodos de ciclo de vida:
@PostConstruct
Após o conteiner instanciar o bean (não significa sair
do pool)
@PreDestroy
Antes do container destruir o bean (não significa voltar
pro pool). Durante a chamada, ainda estão
disponíveis SessionContext e JNDI ENC
. Obter a referência remota não indica que o bean saiu do pool. O bean
sai do pool e é configurado para o cliente
apenas depois que o cliente invoca o primeiro método do bean
--------------------------------------------------------------------------------
------------------------------------------
- Statefull Sessson Beans
@Statefull
public class MyBean implements MyBusinessInterface {...}
. Ciclo de Vida (TODO)
. Precisa de um construtor padrão
. Métodos de ciclo de vida:
@PostConstruct
Após o conteiner instanciar o bean
@PreDestroy
Antes do container destruir o bean. Durante a chamada,
ainda estão
disponíveis SessionContext e JNDI ENC
@PrePassivate
Antes do bean ser apassivado (serializado para
armazenamento de estado). Deve liberar recursos e setar
campos transient com valores nulos.
Objetos que são passivados e gerenciados
automaticamente:
- Tipos primitivos
Página 4
5. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
- Qualquer objeto java serializavel
- SessionContext
- UserTransaction
- javax.naming.Context
- EntityManager
- EntityManagerFactory
- Fabricas injetadas por @Resource
- Referencias injetadas por @EJB
@PostActivate
Depois do bean ser ativado (desserializado para
recuperação de estado). Foge à regra da desserialização
java no que se trata de variáveis transient, cujo valor
assumido será aleatório, e não o valor padrão,
portanto este método deve reabrir os recursos e setar
essas variáveis
. O beans Statefull é removido da memória quando:
- É chamado o método @Remove
- O container detecta que deu timeout e o bean expirou (não pode
expirar durante uma transação)
- @PreDestroy é chamado apenas no caso do @Remove. Em caso de
expiração, depende do fornecedor
a chamada desse método ou não
. Apenas obter a referência remota já cria uma sessão dedicada ao
cliente, porém não cria a instância. A
instância é criada depois de chamar o primeiro método do bean
. No caso de beans Statefull aninhados, a sessão do filho pertence ao
pai, ou seja, se o cliente
remove o pai, o pai remove o filho automaticamente
. Contexto de Persistencia Extendido:
- Pode apenas em bean Statefull
- Faz com que todas as Entidades mantenham-se gerenciadas
durante diferentes invocações de métodos
- Se ele contiver outro bean Statefull aninhado, e esse bean:
tiver interface local e também tiver
contexto extendido, os dois beans compartilham o mesmo contexto
de persistencia extendido
(mesmo EntityManager)
- Entity Manager
@Stateless
public class MyBean implements MyBusinessInterface {
@PersistenceContext(unitName="titan",
type=PersistenceContextType.TRANSACTION)
private EntityManager manager;
...
}
. Atributo type é opcional, o padrão vem TRANSACTION, mas
podemos optar por EXTENDED.
Se for TRANSACTION, diferentes instancias de
EntityManager injetadas em beans diferentes estarão no
mesmo contexto de persistência, até que a transação
geral termine
O tipo EXTENDED pode ser utilizado apenas em beans
@Statefull, e esse contexto dura o mesmo tempo de
vida da instancia do bean (as transações em particular
precisam ser demarcadas).
. Contexto de Persistência
Tempo em que as entidade são gerenciadas pelo EntityManager.
Quando o contexto de persistencia acaba,
todas as entidades tornam-se detached.
Página 5
6. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
. Escopo de Transação
PersistenceContextType.TRANSACTION
O contexto de persistência dura exatamente o tempo de
uma transação JTA, pode apenas ser utilizado
em servidores J2EE, com entidades injetadas via
@PersistenceContext
PersistenceContextType.EXTENDED
O contexto de persistência permanece ativo durante
várias transações, pode ser utilizado apenas em
beans Statefull
. Arquivo persistence.xml
É obrigatório:
<persistence>
<persistence-unit name="titan">
<jta-data-source>java:/OracleDS</jta-data-source>
<properties>
<propertie
name="org.hibernate.hbm2ddl">update</propertie>
</properties>
</persistence-unit>
</persistence>
Obs.:
<persistence-unit>
atributo name – obrigatório
atributo transaction-type – opcional (conf. JTA
para servers J2EE ou RESOURCE_LOCAL para Java SE)
outras tags opcionais:
<description>
<provider> : provedor de persistência (para J2EE
vem o default do container)
<non-jta-data-source>
<mapping-file> : outro arquivo de mapeamento
(container procura orm.xml e outros listados aqui)
<jar-file> : carregar outro jar de entidades
<class> : carregar classes específicas
<exclude-unlisted-classes> : não carregar
qualquer classe que não esteja declarada
no persistence.xml (o jar default é
ignorado)
. Empacotamento
Sempre um arquivo jar com persistence.xml dentro de seu META-INF
(se houver, o arquivo orm.xml também deve
estar nessa pasta)
. Classpath Java SE <JAR AQUI>
. EJB-JAR
. WAR > WEB-INF > lib <JAR AQUI>
. EAR <JAR AQUI>
. EAR > lib <JAR AQUI>
--------------------------------------------------------------------------------
------------------------------------------
- Entity Manager Factory
@Stateless
public class FactoryBean implements FactoryBusinessInterface {
@PersistenceUnit(unitName=”titan”)
private EntityManagerFactory factory1;
private EntityManagerFactory factory2;
Página 6
7. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
@PersistenceUnit(unitName=”toto”)
public void setFactory2(EntityManagerFactory factory2) {...}
}
. Não pode chamar método close() no EntityManagerFactory nem no
EntityManager se eles forem injetados pelo container
(lança IllegalStateException), pois o container cuida dessa limpeza.
. O container injeta em um atributo ou em um setter
. Se criar o EntityManager utilizando o factory, as transações serão de
escopo extendido, ou seja, é preciso
chamar EntityManager.joinTransaction()
--------------------------------------------------------------------------------
------------------------------------------
- Métodos de EntityManager
persist(Object entity)
.Enfileira a entidade para criação no banco (não representa o
momento real do insert)
. É possível chamar persist fora de uma transação apenas se
o contexto for EXTENDED, nesse caso, a inserção é
enfileirada até o contexto ser associado com uma transação
. Se o parâmetro não for uma entidade, lança
IllegalArgumentException
. Se for invocado fora do contexto de transação e for tipo
TRANSACTION, lança uma TransactionRequiredException
joinTransaction()
. Usado apenas se o contexto for criado pela
EntityManagerFactory
. Associa o contexto de persistência à transação
. Obs.: se o contexto for gerenciado pelo container, ele é
associado automaticamente (não precisa desse método)
find(Class<T> entityClass, Object pk) : Entidade
getReference(Class<T> entityClass, Object pk) : Entidade
. Retornam uma entidade a partir de sua chave primária
. O find, se não encontrar retorna null, e utiliza as
configurações de lazy-loading
. O getReference, se não encontrar lança EntityNotFoundException
. Se o contexto de persistência estiver ativo, ela é acoplada,
senão ela é desacoplada
createQuery e createXXXQuery
. Executam querys EJB-QL e consultas nativas (retornam um objeto
Query)
. Entidades retornadas permanegem gerenciadas enquanto o
contexto de persistência estiver ativo
flush()
. Sincroniza as atualizações no banco antes de terminar a
transacao
merge(Object entity) : Entidade
. Atualiza uma entidade desacoplada e retorna uma cópia dela
acoplada
. ATENÇÃO: o objeto de parâmetro nunca é gerenciado pelo
EntityManager, e sim sua cópia que retorna. Se ele
já estiver gerenciado a mesma instancia, ele atualiza ela e
retorna sua referencia.
remove(Object entity)
Remove a entidade da base e torna ela desacoplada
refresh(Object entity)
. Atualiza a entidade acoplada com os dados da base
Página 7
8. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
. Se ela não for acoplada ao próprio EntityManager que invoca o
método, é lançada IllegalArgumentException
. Se o objeto não estiver mais no banco devido outra Thread ou
Processo ter removido ele,
será lançado EntityNotFoundException
contains(Object entity) : Boolean
. Retorna true se a entidade estiver acoplada, false caso
contrario.
clear()
. Desacopla todas as entidades atuais gerenciadas pelo
EntityManager
. Suas modificações são perdidas, portanto é prudente chamar
flush() antes.
setFlushMode(FlushModeType tp)
. Pode receber FlushModeType.AUTO ou FlushModeType.COMMIT
. AUTO - é o padrão, onde a implementação da JPA decide
quando quer fazer o flush(), onde é certo apenas que
ele fará no final da transação, mas também fazer antes
. COMMIT - força para que ele sincronize apenas no final
da transação. COMMIT Melhora a performance pois cada
atualização requer um bloqueio, e fazer todas de uma vez
é mais rápido
getDelegate() : EntityManagerImpl
. Retorna uma instancia ao objeto do fornecedor do implementador
EntityManager, que pode proporcionar
extensões à JPA padrão
--------------------------------------------------------------------------------
------------------------------------------
- EntityTransaction
É a API de transação fornecida como alternativa à JTA. Ela pode ser
usada apenas se o atributo transaction-type
for RESOURCE_LOCAL. Permite utilizar begin(), commit() e rollback()
manualmente.
EntityTransaction et = myEntityManager.getTransaction();
et.begin();
myEntityManager.persist(myEntity);
et.commit();
. O método begin() lança IllegalStateException se já houver uma
transação ativa, commit() e rollback() lançam
a mesma exceção se não houver transação ativa.
- Criando Entidades
@Entity
@Table(name="CUSTOMER_TABLE",
catalog="TITAN",
schema="TITAN",
uniqueConstraints=@UniqueConstraint(columnNames={"EMP_ID",
"EMP_NAME"})
)
public class Customer {
@Id
@GeneratedValue
@Column(name="CUST_ID", nullable=false,
columnDefinition="integer")
private int id;
@Column(name="FIRST_NAME", length=20, nullable=false)
private String firstName;
Página 8
10. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
sequence-name="CUST_SEQ"
initial-value="0"
allocation-size="50"/>
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE"
generator="CUSTOMER_SEQUENCE"/>
</id>
</attributes>
--------------------------------------------------------------------------------
------------------------------------------
- PK Compostas: Por PK Class
public class CustomerPK implements java.io.Serializable {
private String lastName;
private long ssn;
public String getLastName( ) { return this.lastName; }
public void setLastName(String lastName) { this.lastName =
lastName; }
public long getSsn( ) { return ssn; }
public void setSsn(long ssn) { this.ssn = ssn; }
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof CustomerPK)) return false;
CustomerPK pk = (CustomerPK)obj;
if (!lastName.equals(pk.lastName)) return false;
if (ssn != pk.ssn) return false;
return true;
}
public int hashCode( ) {
return lastName.hashCode( ) + (int)ssn;
}
}
@Entity
@IdClass(CustomerPK.class)
public class Customer {
private String firstName;
private String lastName;
private long ssn;
@Id
public String getLastName( ) { return lastName; }
public void setLastName(String lastName) { this.lastName =
lastName; }
@Id
public long getSsn( ) { return ssn; }
public void setSsn(long ssn) { this.ssn = ssn; }
}
Requerimentos para a PK Class:
. It must be serializable
. It must have a public no-arg constructor
. It must implement the equals( ) and hashCode( ) methods.
- PK Compostas: Por Embeddable Class
@Embeddable
public class CustomerPK implements java.io.Serializable {
private String lastName;
private long ssn;
Página 10
11. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
public String getLastName( ) { return this.lastName; }
public void setLastName(String lastName) { this.lastName =
lastName; }
public long getSsn( ) { return ssn; }
public void setSsn(long ssn) { this.ssn = ssn; }
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof CustomerPK)) return false;
CustomerPK pk = (CustomerPK)obj;
if (!lastName.equals(pk.lastName)) return false;
if (ssn != pk.ssn) return false;
return true;
}
public int hashCode( ) {
return lastName.hashCode( ) + (int)ssn;
}
}
@Entity
public class Customer {
private String firstName;
private CustomerPK pk;
@EmbeddedId
public PK getPk( ) { return pk; }
public void setPk(CustomerPK pk) { this.pk = pk; }
}
Obs.: Podemos sobrescrever as anotações @Column feitas na classe da PK
(não podemos utilizar @Column na classe entidade,
pois o nome da coluna á definida na classe de PK)
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name="lastName",
column=@Column(name="LAST_NAME"),
@AttributeOverride(name="ssn",
column=@Column(name="SSN"))
})
--------------------------------------------------------------------------------
------------------------------------------
- Mapeando Atributos
@Transient
Campo não é criado na base
@Basic(fetch=FetchType.LAZY, optional=false)
Se o campo é obrigatório, e permite lazy loading do campo (porém
a especificação não garante o lazy loading real,
depende da implementação)
@Temporal(TemporalType.TIME)
Define o tipo real a gerar na base para um tipo de data java
(DATE, TIME, TIMESTAMP)
@Lob
Define que será um tipo binário (blob) ou um tipo sequencial de
caracteres (clob). O tipo real gerado depende do
tipo de atributo:
BLOB: byte[], Byte[], or java.io.Serializable
CLOB: char[], Character[], or java.lang.String
@Enumerated(EnumType.STRING)
Página 11
12. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
Para mapear um atributo do tipo enum. Pode ser ORDINAL ou
STRING, onde ORDINAL grava o numero do indice do campo
no enum e STRING grava o proprio nome do enum.
--------------------------------------------------------------------------------
------------------------------------------
- Mapeamentos Multi-Table
@Entity
@SecondaryTable(name="ADDRESS_TABLE",
pkJoinColumns={@PrimaryKeyJoinColumn(name="ADDRESS_ID")})
public class Customer {
private long id;
private String firstName;
private String street;
private String city;
...
@Column(name="STREET", table="ADDRESS_TABLE")
public String getStreet( ) { return street; }
public void setStreet(String street) { this.street = street; }
@Column(name="CITY", table="ADDRESS_TABLE")
public String getCity( ) { return city; }
public void setCity(String city) { this.city = city; }
...
}
Obs.: É suportado mais de uma tabela secundária:
@SecondaryTables({
@SecondaryTable(name="ADDRESS_TABLE",
pkJoinColumns={@PrimaryKeyJoinColumn (name="ADDRESS_ID")}),
@SecondaryTable(name="CREDIT_CARD_TABLE",
pkJoinColumns={@PrimaryKeyJoinColumn (name="CC_ID")})
})
--------------------------------------------------------------------------------
------------------------------------------
- Campos de Embeddable Objects
@Embeddable
public class Address {
private String street;
private String city;
private String state;
}
@Entity
public class Customer {
private long id;
private String firstName;
private String lastName;
@Embedded
private Address address;
}
Obs.: como as colunas geradas pela classe embedded são definidas dentro
dela, também vale o conceito de
@AttributeOverrides para definir outras caracteristicas de coluna dentro
da classe utilizadora
--------------------------------------------------------------------------------
------------------------------------------
- Unidirecional "Um-Para-Um"
Página 12
16. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
private Set<Customer> customers = new HashSet<Customer>( );
...
}
@Entity
public class Customer {
...
@ManyToMany(mappedBy="customers")
private Collection<Reservation> reservations = new ArrayList<Reservation>(
);
...
}
<entity-mappings>
<entity class="com.titan.domain.Reservation" access="FIELD">
<attributes>
<id name="id">
<generated-value/>
</id>
<many-to-many name="customers"
target-entity="com.titan.domain.Customer" fetch="LAZY">
<join-table name="RESERVATION_CUSTOMER">
<join-column name="RESERVATION_ID"/>
<inverse-join-column
name="CUSTOMER_ID"/>
</join-table>
</many-to-many>
</attributes>
</entity>
<entity class="com.titan.domain.Customer" access="FIELD">
<attributes>
<id name="id">
<generated-value/>
</id>
<many-to-many name="cruise"
target-entity="com.titan.domain.Reservation" fetch="LAZY" mapped-by="customers"
/>
</attributes>
</entity>
</entity-mappings>
--------------------------------------------------------------------------------
------------------------------------------
@PrimaryKeyJoinColumn
Utilizado no OneToOne, quando queremos que a associação não
utilize um campo de chave estrangeira, neste
caso a associação será feita gravando IDs de mesmo valor nas
duas tabelas
@PrimaryKeyJoinColumns({
@PrimaryKeyJoinColumn(name="ADDR_ID"),
@PrimaryKeyJoinColumn(name="ADDR_ZIP")
})
@JoinColumn(name="TOTO_ID")
Indica a coluna que receberá a chave primária da outra tabela.
Se quiser que referencia outra coluna que não
seja a chave primária da tabela, utilizar o atributo
referencedColumnName (porem coluna nao pode ter repetição)
@JoinColumns({
@JoinColumn(name="ADDR_ID"),
@JoinColumn(name="ADDR_ZIP")
})
É utilizado para chaves primárias compostas por mais de uma
coluna
Página 16
17. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
@JoinTable
É utilizada uma tabela de junção de IDs para relacionamentos
Um-Para-Muitos e Muitos-Para-Um
Um-Para-Muitos: @JoinTable é padrão
Muitos-Para-Um: @JoinColumn é padrão
Muitos-Para-Muitos: apenas @JoinTable
@OrderBy("lastname ASC, firstname DESC")
Permite ordenação, serve apenas para relacionamento com List
@MapKey(name="number")
É utilizado para relacionamento com Map. O atributo name será o
da key do Map, e a entidade relacionada será o value
FetchType.EAGER e FetchType.LAZY (cuidados)
Se a entidade estiver "detached" e seu relacionamento LAZY não
for carregado, e tanter ser acessado, será lançada
uma exceção do provedor de persistência (uma chamada ao .size()
da coleção do relacionamento garante o load da coleção,
ou o uso do operador FETCH JOIN da ejbql)
Cruise detachedCruise = ... ;
try {
int numReservations =
detachedCruise.getReservations().size();
} catch (SomeVendorLazyInitializationException ex) { }
CascadeType - ALL, PERSIST, MERGE, REMOVE, REFRESH
Realiza a respectiva operação do EntityManager para todas as
entidades relacionadas. Aumenta a carga de calls na base e
pode causar efeitos indesejáveis
- Herança: Single Table Per Class
Schema: é gerada uma tabela contendo todos os atributos de toda a
hierarquia, adicionando
uma coluna discriminadora, que identifica qual é o tipo.
Person > Customer > Employee
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DISCRIMINATOR",
discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("PERSON")
public class Person { ... }
@Entity
@DiscriminatorValue("CUST")
public class Customer extends Person { ... }
@Entity
public class Employee extends Customer { ... }
Obs.:
@DiscriminatorColumn
Pode ser STRING, CHAR ou INTEGER. Não é obrigatório, se
omitido o default é STRING e o nome é gerado
pelo fornecedor.
@DiscriminatorValue
Não é obrigatório, se omitido utiliza o nome da classe.
Vantagens:
É simples e performática, não requer joins e é boa para
selects polimorficos.
Desvantagens:
Todas as colunas de todas as subclasses precisam ser
NULLABLE, e não possui um modelo normalizado.
Página 17
18. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
- Herança: Table Per Concrete Class
Schema: é gerada uma tabela para cada classe concreta, cada tabela
contendo todos os atributos dessa
classe e todos seus atributos herdados.
Person > Customer > Employee
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Person { ... }
@Entity
public class Customer extends Person { ... }
@Entity
public class Employee extends Customer { ... }
Obs.:
Vantagens:
Pode ter constraints NOT NULL em qualquer campo, e o
legado pode estar modelado dessa forma.
Desvantagens:
É mais lento, não é normalizado, possui campos
redundantes e tem baixa performance num
select polimorfico.
- Herança: Table Per SubClass
Schema: é gerada uma tabela para cada classe, cada tabela contendo
apenas os atributos da classe, onde
todas as tabelas são associadas pelas suas chaves primárias.
Person > Customer > Employee
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Person { ... }
@Entity
public class Customer extends Person { ... }
@Entity
@PrimaryKeyJoinColumn(name="EMP_PK")
public class Employee extends Customer { ... }
Obs.:
A anotação @PrimaryKeyJoinColumn é obrigatória se as colunas das
chaves primárias possuirem nomes diferentes
nas tabelas.
Vantagens:
Pode ter constraints NOT NULL em qualquer campo e seu
modelo é normalizado.
Desvantagens:
É mais lento que a SINGLE_TABLE.
- Herança: Nonentity Base Classes
@MappedSuperclass
public class Person {
@Id
@GeneratedValue
private int id;
private String lastname;
...
}
Página 18
19. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
@Entity
@AttributeOverride(name="lastname", column=@Column(name="SURNAME"))
public class Customer extends Person { ... }
Obs.:
A superclasse não é uma entidade e nem é persistida, ela apenas
fornece atributos persistentes
para as suas subclasses, que podem ser sobrecarregados com o
@AttributeOverrides / @AttributeOverride
- Metodos de Callback
@PrePersist
- Quando uma entidade é agendada para inserção no banco de dados
. Depois de persist()
. Depois de merge() caso haja inserção aninhada em
cascata
@PostPersist
- Quando a inserção real no banco de dados ocorre efetivamente
. Depois do flush() se houver inserção infileirada
. Depois que o contexto de persistencia atualiza o banco
de dados
(se for FlushType.COMMIT, é no final da transação)
@PostLoad
- Depois da entidade ser hidradata com os dados da base
. Depois do find(), getReference() ou uma consulta
EJB-QL
@PreUpdate
- Antes da entidade ser atualizada no banco de dados
. Antes do flush() ou antes de o contexto de
persistencia atualizar o banco de dados
@PostUpdate
- Depois da entidade ser atualizada no banco de dados
. Depois do flush() ou depois de o contexto de
persistencia atualizar o banco de dados
@PreRemove
- Quando uma entidade é agendada para remoção do banco de dados
. Depois de remove()
. Depois de merge() caso haja remoção aninhada em
cascata
@PostRemove
- Quando a exclusão real no banco de dados ocorre efetivamente
. Depois do flush() se houver exclusão infileirada
. Depois que o contexto de persistencia atualiza o banco
de dados
(se for FlushType.COMMIT, é no final da transação)
Obs.:
Na classe de entidade: qualquer método que retorne void e não
receba argumentos
Na classe listener: qualquer método que retorne void e receba
como argumento um Object (conterá a instância
da entidade em questão)
--------------------------------------------------------------------------------
------------------------------------------
- Listeners de Entidade
public class MeuListener1 {
...
@PostPersist
Página 19
20. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
public void postInsert(Object entity) { ... }
...
}
public class MeuListener2 {
...
@PostPersist
public void postPersist(Object entity) { ... }
@PostLoad
public void postLoad(Object entity) { ... }
...
}
@Entity
@EntityListeners({MeuListener1.class, MeuListener2.class})
public class MyEntity {
@PostPersist
public void afterInsert() { ... }
}
Obs.:
Chamados na ordem em que foram declarados em @EntityListeners ou
no XML, por ultimo o da entidade
postInsert() + postPersist() + afterInsert()
- Herança de Listeners
@Entity
@EntityListeners({MeuListener1.class})
public class Person {
@PostPersist
public void parentAfterInsert() { ... }
}
@Entity
@EntityListeners({MeuListener2.class})
public class Customer extends Person {
@PostPersist
public void childAfterInsert() { ... }
}
Obs.:
Ordem de chamada:
PostPersist de MeuListener1
PostPersist de MeuListener2
PostPersist de Person
PostPersist de Customer
Outras anotations:
@ExcludeDefaultListeners
Ignora os "Listeners Padrão" setados na unidade
de persistencia
@ExcludeSuperclassListeners
Ignora os listeners herdados
- Declarando Listeners com XML
<entity class="com.titan.domain.Cabin">
<entity-listeners>
<entity-listener
class="com.titan.listeners.TitanAuditLogger" />
<entity-listener
class="com.titan.listeners.EntityJmxNotifier">
<pre-persist name="beforeInsert"/>
<post-load name="afterLoading"/>
</entity-listener>
Página 20
21. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
</entity-listeners>
</entity>
- Declarando "Listeners Padrão"
Os listeners declarados dessa forma serão aplicados à todas entidades da
unidade de persistencia:
<entity-mappings>
<entity-listeners>
<entity-listener
class="com.titan.listeners.EntityJmxNotifier">
<pre-persist name="beforeInsert"/>
<post-load name="afterLoading"/>
</entity-listener>
</entity-listeners>
</entity-mappings>
- Conceitos Gerais EJBQL
. Pode comparar primitivo com Wrapper e comparar valores temporais
. Comparação de entidades na base é feita pela chave primaria
. Clausula IN no from: itera a lista e faz join (corresponde a INNER
JOIN ou somente JOIN)
. Operadores valem apenas no WHERE
. Clausula IN no where: verifica se valor está dentro da lista de
LITERAIS: IN('X', 'Y', 'Z')
. Relacionamento "Um-Para-Um" ou campo basico: IS NULL
. Relacionamento baseado em coleção (muitos): IS EMPTY
. Cuidado INNER JOIN com IS EMPTY (pag 136)
. Cuidado: order by e campos que pode (pag 141)
. Operações em lote (chamar antes: flush e clear)
- Interface Query
public interface Query {
public List getResultList( );
public Object getSingleResult( );
public int executeUpdate( );
public Query setMaxResults(int maxResult);
public Query setFirstResult(int startPosition);
public Query setHint(String hintName, Object value);
public Query setParameter(String name, Object value);
public Query setParameter(String name, Date value, TemporalType
temporalType);
public Query setParameter(String name, Calendar value, TemporalType
temporalType);
public Query setParameter(int position, Object value);
public Query setParameter(int position, Date value, TemporalType
temporalType);
public Query setParameter(int position, Calendar value, TemporalType
temporalType);
public Query setFlushMode(FlushModeType flushMode);
}
. Retornando Resultados
Método getSingleResult
try {
Query query = entityManager.creatQuery(
"from Customer c where c.firstName='Bill' and
c.lastName='Burke'");
Customer cust = (Customer)query.getSingleResult(
);
} catch (EntityNotFoundException notFound) {
} catch (NonUniqueResultException nonUnique) {}
- Utilizado para retornar apenas um registro:
. Não encontrou nenhum registro:
Página 21
22. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
EntityNotFoundException
. Encontrou mais de um registro:
NonUniqueResultException
Método getResultList
Query query = entityManager.creatQuery(
"from Customer c where c.firstName='Bill' and
c.lastName='Burke'");
List bills = query.getResultList( );
- Utilizado para retornar uma coleção de registros:
. Não encontrou nenhum registro: retorna uma
lista vazia
. Parametros
Parametros Nomeados
Query query = entityManager.createQuery(
"from Customer c where c.firstName=:first and
c.lastName=:last");
query.setParameter("first", first);
query.setParameter("last", last);
Parametros Indexados
Query query = entityManager.createQuery(
"from Customer c where c.firstName=?1 and c.lastName=?2");
query.setParameter(1, first);
query.setParameter(2, last);
. Parametros Temporais
Query query = entityManager.createQuery(
"from Customer c where c.dataNascimento=?1");
query.setParameter(1, new java.util.Date,
TemporalType.DATE); //DATE, TIME, TIMESTAMP
. Paginação de Registros
setFirstResult: seta o índice do primeiro registro a trazer
setMaxResults: seta a quantidade máxima de registros a trazer
Query query = entityManager.createQuery("from Customer
c");
List customers =
query.setMaxResults(max).setFirstResult(index).getResultList( );
Obs.: é perigoso trazer muitos registros de uma vez, pois todos
ficam gerenciados pelo EntityManager
e o sistema fica pesado, recomenda-se trazer aos poucos dando
clear() no EntityManager
. Hints
Utilizado para setar na query um atributo específico de
fornecedor
Query query = manager.createQuery("from Customer c");
query.setHint("org.hibernate.timeout", 1000);
. FlushMode
query.setFlushMode(FlushModeType.COMMIT);
Indica que não deve haver nenhum flush antes da execução
desta query
query.setFlushMode(FlushModeType.AUTO);
É o default, deixa o container livre para fazer flush
antes da query, se desejar
--------------------------------------------------------------------------------
------------------------------------------
- Clausulas EJB-QL
Página 22
23. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
. Considerações Gerais
- As clausulas não são case-sensitive
- Os nomes de classes e atributos são case-sensitive
. Abstract Schema Name
@Entity public class Customer {...} ->
entityManager.createQuery("SELECT c FROM Customer AS c");
@Entity(name="Cust") public class Customer {...} ->
entityManager.createQuery("SELECT c FROM Cust AS c");
. Simple Queryes
SELECT OBJECT( c ) FROM Customer AS c
SELECT c FROM Customer AS c
SELECT c FROM Customer c
ERRO: SELECT customer FROM Customer AS customer
O identificador não pode ter nome igual ao nome da
entidade, independente do case dos caracteres
SELECT c.firstName, c.lastName FROM Customer AS c
Obs.: se a entidade for mapeada como tipo FIELD, acessa
por nome do atributo, se for como
tipo PROPERTY, acessa pelo nome do método JavaBean
transformado em nome de atributo
Obs.:
Se a query retornar uma entidade, os registros retornam
como um List de entidades
Se a query retornar campos escalares, os registros
retornam como um List de Object[]
. Navegando Entidades
SELECT c.creditCard.creditCompany.address FROM Customer AS c
Efeito: Retorna o "address" da "creditCompany" da
"creditCard" de todos "Customer"
Obs.: Permite navegar por relacionamento "unário" ou por
atributo @Embedded
Exemplo 1:
@Entity
public class Address {
private ZipCode zip;
}
ERRO: SELECT c.address.zip.mainCode FROM
Customer AS c
Para tornar o select válido, deveria modificar o
atributo para:
@Embedded
private ZipCode zip;
Exemplo 2:
@Entity
public class Customer {
@OneToMany
private List<Phone> phones;
}
ERRO: SELECT c.phones.number FROM Customer AS c
Isso seria o mesmo que customer.getPhones(
).getNumber( ); o que é INVALIDO.
. Constructor Expressions
Página 23
24. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
public class Name {
private String first;
private String last;
public Name(String first, String last) {
this.first = first;
this.last = last;
}
}
SELECT new myPackage.Name(c.firstName, c.lastName) FROM Customer
c
Efeito: Retorna uma List de objetos "Name" preenchidos
como indicado no construtor
. Clausula IN / INNER JOIN / JOIN / LEFT JOIN / XXX JOIN FETCH /
DISTINCT
SELECT r FROM Customer AS c, IN( c.reservations ) r
Efeito: Retorna todas reservas de todos clientes
Obs.: É idêndico a:
= SELECT r FROM Customer AS c, INNER JOIN
c.reservations r
= SELECT r FROM Customer AS c, JOIN
c.reservations r
SELECT c.firstName, c.lastName, p.number FROM Customer c INNER
JOIN c.phoneNumbers p
Como no SQL, INNER JOIN irá retornar apenas campos onde
existe associação não nula,
ou seja, irá retornar apenas clientes que possuem
telefones associados
SELECT c.firstName, c.lastName, p.number FROM Customer c LEFT
JOIN c.phoneNumbers p
Como no SQL, LEFT JOIN irá retornar também campos onde
existe associação nula,
ou seja, irá retornar todos clientes e, caso não possua
telefone associado, retornará null
no objeto associativo
SELECT c FROM Customer c LEFT JOIN FETCH c.phones
Adicionando FETCH após o JOIN indica que se o
relacionamento for LAZY, deverá carregar mesmo assim
Evita o problema N+1, onde se executassemos getPhones()
seriam feito N selects, desta forma é feito
apenas um select.
SELECT DISTINCT cust FROM Reservation AS res, IN (res.customers)
cust
Não retorna instancias duplicadas no resultado
. Clausula WHERE / BETWEEN / IN / LIKE / IS NULL / IS EMPTY / MEMBER OF
SELECT c FROM Customer AS c WHERE
c.creditCard.creditCompany.name = 'Capital One'
SELECT s FROM Ship AS s WHERE s.tonnage = 100000.00
SELECT c FROM Customer AS c WHERE c.hasGoodCredit = TRUE
SELECT r FROM Reservation AS r WHERE (r.amountPaid * .01) >
300.00
SELECT r FROM Reservation r, IN ( r.customers ) AS cust WHERE
cust = :specificCustomer
SELECT s FROM Ship AS s WHERE s.tonnage BETWEEN 80000.00 AND
130000.00
SELECT c FROM Customer AS c WHERE c.address.state IN ('FL',
'TX', 'MI', 'WI', 'MN')
SELECT c FROM Customer AS c WHERE c.address.state NOT IN ('FL',
Página 24
25. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
'TX', 'MI', 'WI', 'MN')
SELECT c FROM Customer AS c WHERE c.address.state IN ( ?1, ?2,
?3, 'WI', 'MN')
SELECT c FROM Customer AS c WHERE c.address IS NULL
SELECT c FROM Customer AS c WHERE c.address IS NOT NULL
SELECT crs FROM Cruise AS crs WHERE crs.reservations IS EMPTY
SELECT crs FROM Cruise AS crs WHERE crs.reservations IS NOT
EMPTY
Obs.: Não pode utilizar IS EMPTY em um campo especificado no
JOIN
ERRO: SELECT r FROM Reservation AS r INNER JOIN
r.customers AS c
WHERE r.customers IS NOT EMPTY AND
c.address.city = 'Boston'
Obs.: Na clausula like, % indica N caracteres e _ indica um
caractere
SELECT OBJECT( c ) FROM Customer AS c WHERE c.lastName LIKE
'%-%'
SELECT OBJECT( c ) FROM Customer AS c WHERE c.lastName LIKE
'Joan_'
Obs.: Clausula MEMBER OF é como se fosse o IN para tipos de
coleção (contains)
SELECT crs FROM Cruise AS crs, IN (crs.reservations) AS res,
Customer AS cust
WHERE cust = :myCustomer AND cust MEMBER OF
res.customers
. Clausulas Funcionais
LOWER(String)
UPPER(String)
TRIM([[LEADING | TRAILING | BOTH] [trim_char] FROM] String)
CONCAT(String1, String2)
LENGTH(String)
LOCATE(String1, String2 [, start])
SUBSTRING(String1, start, length)
ABS(number)
SQRT(double)
MOD(int, int)
CURRENT_DATE
CURRENT_TIME
CURRENT_TIMESTAMP
. Clausulas Agregadoras
Obs.: se COUNT avalia uma coleção vazia, ele retorna zero
SELECT COUNT( c ) FROM Customers AS c WHERE c.address.state =
'WI'
SELECT COUNT(c.address.zip) FROM Customers AS c WHERE
c.address.zip LIKE '554%'
Obs.: MAX e MIN comparam qualquer tipo de dado (utiliza valor ou
tamanho em bytes)
SELECT MAX( r.amountPaid ) FROM Reservation AS r
SELECT MAX( r.amountPaid ) FROM Reservation AS r
Obs.: SUM e AVG servem apenas para numericos e wrappers
SELECT SUM(r.amountPaid) FROM Cruise c join c.reservations r
WHERE c = :cr
Obs.: quando utilizado com agregação, o DISTINCT é aplicado
antes de agregar os dados
Página 25
26. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
SELECT DISTINCT COUNT(c.address.zip) FROM Customers AS c WHERE
c.address.zip LIKE '554%'
Efeito: conta apenas os registros com zip diferentes
SELECT c FROM Customers AS c ORDER BY c.lastName
SELECT c FROM Customers AS c WHERE c.address.city = 'Boston' AND
c.address.state = 'MA' ORDER BY c.lastName
SELECT c FROM Customers AS c ORDER BY c.lastName DESC
SELECT c FROM Customers AS c ORDER BY c.lastName ASC,
c.firstName DESC
Cuidado:
SELECT addr.zip FROM Address AS addr ORDER BY addr.zip
SELECT c.address FOR Customer AS c WHERE c.lastName =
'Smith' ORDER BY c.address.zip
ERRO: SELECT c FROM Customer AS c ORDER BY
c.address.city
Se a query retorna uma coleção, no campo order
by podem ser utilizados apenas
campos basicos desta coleção
ERRO: SELECT c.address.city FROM Customer AS c ORDER BY
c.address.state
O campo especificado em order by precisa estar
sendo trazido no select
SELECT cr.name, COUNT (res) FROM Cruise cr JOIN cr.reservations
res GROUP BY cr.name
HAVING count(res) > 10
. Subqueries
SELECT COUNT(res) FROM Reservation res WHERE res.amountPaid >
(SELECT avg(r.amountPaid) FROM Reservation r)
FROM Cruise cr WHERE 100000 < ( SELECT SUM(res.amountPaid) FROM
cr.reservations res )
FROM Cruise cr WHERE 0 < ALL ( SELECT res.amountPaid from
cr.reservations res )
Efeito: Seleciona cruzeiros em que todas suas reservas
possuam valor de pagamento
FROM Cruise cr WHERE 0 = ANY ( SELECT res.amountPaid from
cr.reservations res )
Efeito: Seleciona cruzeiros em que exista ao menos uma
reserva com valor zerado
Obs.: SOME = ANY = NOT ALL
FROM Cruise cr WHERE EXISTS (SELECT res FROM cr.reservations
WHERE res.amountPaid = 0)
Obs.: Clausula EXISTS verifica se a subquery retornou
algum registro
Efeito: Seleciona os cruseiros que tenham alguma reserva
sem pagamento
. UPDATE e DELETE em lote
UPDATE Reservation res SET res.amountPaid = (res.amountPaid +
10)
WHERE EXISTS ( SELECT c FROM res.customers c WHERE c.firstName =
'Bill' AND c.lastName='Burke' )
DELETE FROM Reservation res WHERE EXISTS ( SELECT c FROM
res.customers c WHERE c.firstName = 'Bill'
AND c.lastName='Burke' )
Página 26
27. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
- Scalar Native Queries
Query createNativeQuery(String sql)
. Usada para valores escalares, retorna um List de Object[] contendo os
valores
- Simple Entity Native Queries
Query createNativeQuery(String sql, Class entityClass)
Query query = manager.createNativeQuery("SELECT p.phone_PK,
p.phone_number, p.type
FROM PHONE AS p", Phone.class);
. Espera que os nomes dos campos retornados correspondam em nome e tipo
com os atributos persistentes
da entidade em questão, então a associação é feita automaticamente
. É obrigatório retornar TODOS os atributos da entidade na query
- Complex Native Queries
Query createNativeQuery(String sql, String mappingName)
@Entity
@SqlResultSetMapping(name="customerAndCreditCardMapping",
entities={ @EntityResult(entityClass=Customer.class),
@EntityResult(entityClass=CreditCard.class,
fields={@FieldResult(name="id",
column="CC_ID"),
@FieldResult(name="number", column="number")}
)})
public class Customer {...}
Query query = manager.createNativeQuery( "SELECT c.id, c.firstName,
cc.id As CC_ID, cc.number
FROM CUST_TABLE c, CREDIT_CARD_TABLE cc WHERE c.credit_card_id =
cc.id",
"customerAndCreditCardMapping");
. O mapeamento deve estar definido na entidade
. Mapeamento via XML:
<entity-mappings>
<sql-result-set-mapping
name="customerAndCreditCardMapping">
<entity-result
entity-class="com.titan.domain.Customer"/>
<entity-result
entity-class="com.titan.domain.CreditCard"/>
<field-result name="id" column="CC_ID"/>
<field-result name="number"
column="number"/>
</entity-result>
</sql-result-set-mapping>
</entity-mappings>
. Misturando resultado de Entidade e resultado Escalar:
@SqlResultSetMapping(name="reservationCount",
entities=@EntityResult(name="com.titan.domain.Cruise",
fields=@FieldResult(name="id", column="id")),
columns=@ColumnResult(name="resCount"))
@Entity
public class Cruise {...}
--------------------------------------------------------------------------------
Página 27
28. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
------------------------------------------
- Queryes Nomeadas
. EJBQL Named Queryes
@NamedQueries({
@NamedQuery(name="getAverageReservation",
query="SELECT AVG( r.amountPaid) FROM Cruise As
c, JOIN c.reservations r WHERE c = :cruise"),
@NamedQuery(name="findFullyPaidCruises",
query="FROM Cruise cr WHERE 0 < ALL (SELECT
res.amountPaid from cr.reservations res)")
})
@Entity
public class Cruise {...}
Ou Via XML:
<entity-mappings>
<named-query name="getAverageReservation">
<query>
SELECT AVG( r.amountPaid) FROM
Cruise As c JOIN c.reservations r WHERE c = :cruise
</query>
</named-query>
</entity-mappings>
. SQL Native Named Queryes
@NamedNativeQuery(name="findCustAndCCNum",
query="SELECT c.id, c.firstName, c.lastName, cc.number
AS CC_NUM
FROM CUST_TABLE c, CREDIT_CARD_TABLE cc
WHERE c.credit_card_id = cc.id",
resultSetMapping="customerAndCCNumMapping")
@SqlResultSetMapping(name="customerAndCCNumMapping",
entities={@EntityResult(entityClass=Customer.class)},
columns={@ColumnResult(name="CC_NUM")}
)
@Entity
public class Customer {...}
Ou via XML:
<entity-mappings>
<named-native-query name="findCustAndCCNum"
result-set-mapping="customerAndCCNumMapping">
<query>
SELECT c.id, c.firstName,
c.lastName, cc.number AS CC_NUM FROM CUST_TABLE c,
CREDIT_CARD_TABLE cc WHERE
c.credit_card_id = cc.id
</query>
</named-native-query>
</entity-mappings>
- Sistema JMS
. Provedor JMS
Sistema de rotamento de mensagens
. Clientes JMS
Produtor (envia) e consumidor (recebe)
. Características JMS
- Assíncrono (cliente não espera resposta)
- Não é propagada transação do produtor pro receptor
- Não é propagada segurança nem credenciais do produtor pro
receptor
- Se o receptor estiver desconectado, o roteador garante a
Página 28
29. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
entrega da mensagem
- Ciclo de Vida: idêntico ao do Stateless Bean, também em pool
de instâncias
(também com @PostConstruct e @PreDestroy)
@Resource(mappedName="ConnectionFactoryName")
private ConnectionFactory connectionFactory;
@Resource(mappedName="TopicName")
private Topic topic;
...
Connection connect = factory.createConnection( );
Session session = connect.createSession(true, 0); // 0 ->
Session.AUTO_ACKNOWLEDGE
MessageProducer producer = session.createProducer(topic);
TextMessage textMsg = session.createTextMessage( );
textMsg.setText("my message");
producer.send(textMsg);
connect.close( );
. ConnectionFactory
Fornece uma factory para criar conexões JMS com um provedor
específico
. Connection
Conexão efetica com o provedor JMS
. Session
- Sessão para agrupar ações de envio/recebimento de mensagens
- Método createSession(boolean transacted, int acknowledgeMode)
- muitos provedores utilizam true
e 0 (Session.AUTO_ACKNOWLEDGE) por padrão, porém não é garantido
- Obs.: O objeto session não é thread-safe, pode apenas um
objeto session JMS por Thread
. Topic / Queue
Nome do channel do Topic ou da Queue.
- Topic
. Tipo publish/subscribe
. Muitos receptores (a mensagem é entregue à
todos que assinam)
. Modelo push (o receptor é notificado da
mensagem)
- Queue:
. Tipo peer-to-peer
. Um receptor (a mensagem é entregue apenas uma
vez, ao primeiro que receber)
. Conceitualmente é pull (receptor solicita a
mensagem), mas também pode ser push, depende do
fornecedor
. Tipos de Mensagem
- Sempre compostas por um Header e um Corpo:
TextMessage textMsg = session.createTextMessage();
MapMessage mapMsg = session.createMapMessage();
ObjectMessage objMsg = session.createObjectMessage();
(outras...)
- Exemplo de Header:
textMessage.setJMSReplyTo(theAnotherQueue);
. Cliente JMS Java SE
ConnectionFactory factory = (ConnectionFactory)
jndiContext.lookup("ConnectionFactoryNameGoesHere");
Topic topic = (Topic)jndiContext.lookup("TopicNameGoesHere");
Connection connect = factory.createConnection( );
Session session = connect.createSession(false,
Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(topic);
Página 29
30. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
consumer.setMessageListener(this); //ou qualquer instancia que
implemente javax.jms.MessageListener
connect.start( );
public void onMessage(Message message) {
TextMessage textMsg = (TextMessage)message;
String text = textMsg.getText();
}
- Message Driven Beans
. Bean especial para consumir mensagens
. Não se deve utilizar um Session Bean para consumir mensagens (mas é
possível, através dos métodos
MessageConsumer.receive(), MessageConsumer.receive(long timeout) ou
MessageConsumer.receiveNoWait()
. Anotação
@Target(TYPE) @Retention(RUNTIME)
public @interface MessageDriven {
String name() default ""; //nome do MDB
Class messageListenerInterface default Object.class;
//pode setar como javax.jms.MessageListener e não
//implementar a interface explicitamente
ActivationConfigProperty[] activationConfig() default
{}; //configurações específicas de MDB
String mappedName(); //se preferir pode especificar o
destino aqui
String description();
}
. MessageDrivenContext
- Extende o EJBContext, é semelhante ao SessionContext porém não
adiciona nenhuma operação
- Pode-se utilizar apenas os métodos transacionais. Os métodos
de segurança lançam uma
RuntimeException, pois na JMS não há propagação de segurança
. Interface javax.jms.MessageListener
Único método: public void onMessage(Message message)
@MessageDriven(
activationConfig={
@ActivationConfigProperty(
propertyName="destinationType",
propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(
propertyName="destinationName",
propertyValue="jms/TitanQueue"),
@ActivationConfigProperty(
propertyName="messageSelector",
propertyValue="MessageFormat = 'Version 3.4'"),
@ActivationConfigProperty(
propertyName="acknowledgeMode",
propertyValue="Auto-acknowledge")
})
public class ReservationProcessorBean implements
javax.jms.MessageListener {
@Resource(mappedName="ConnectionFactory")
private ConnectionFactory connectionFactory;
public void onMessage(Message message) {
try {
MapMessage reservationMsg = (MapMessage)message;
Página 30
31. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
int customerPk =
reservationMsg.getInt("CustomerID");
int cruisePk =
reservationMsg.getInt("CruiseID");
int cabinPk = reservationMsg.getInt("CabinID");
double price =
reservationMsg.getDouble("Price");
} catch(Exception e) { throw new EJBException(e); }
}
}
. Atributos ActivationConfigProperty
- messageSelector
Pode utilizar uma query sintaxe SQL para filtrar quais
mensagens o bean vai consumir de acordo com
propriedades setadas na mensagem
- acknowledgeMode
AUTO_ACKNOWLEDGE - é computado recebimento logo após a
mensagem ser entregue e processada
DUPS_OK_ACKNOWLEDGE - é computado o recebimento em lote,
de forma otimizada (o MDB precisa ter
preparo para tratar mensagens duplicatas)
- subscriptionDurability
Durable - tolerantes à desconexão do container, a
mensagem é armazenada e entregue depois
NonDurable - o contrário, qualquer mensagem recebida
enquanto desconectado será perdida
. Descritor XML
<enterprise-beans>
<message-driven>
<ejb-name>ReservationProcessorBean</ejb-name>
<ejb-class>com.titan.reservationprocessor.ReservationProcessorBean</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Container</transaction-type>
<message-destination-type>javax.jms.Queue</message-destination-type>
<activation-config>
<activation-property>
<activation-config-property-name>destinationType</activation-config-property-nam
e>
<activation-config-property-value>javax.jms.Queue</activation-config-property-va
lue>
<activation-property>
<activation-property>
<activation-config-property-name>destinationName</activation-config-property-nam
e>
<activation-config-property-value>jms/TitanQueue</activation-config-property-val
ue>
<activation-property>
<activation-property>
<activation-config-property-name>messageSelector</activation-config-property-nam
e>
<activation-config-property-value>MessageFormat = 'Version
3.4'</activation-config-property-value>
<activation-property>
<activation-property>
Página 31
32. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
<activation-config-property-name>acknowledgeMode</activation-config-property-nam
e>
<activation-config-property-value>Auto-acknowledge</activation-config-property-v
alue>
<activation-property>
</activation-config>
</message-driven>
</enterprise-beans>
</ejb-jar>
--------------------------------------------------------------------------------
------------------------------------------
- Ouvindo um Timer
@Stateless
public class ShipMaintenanceBean implements ShipMaintenanceRemote
implements javax.ejb.TimedObject {
...
public void ejbTimeout(javax.ejb.Timer timer) { ... }
}
ou...
@Stateless
public class ShipMaintenanceBean implements ShipMaintenanceRemote {
...
@Timeout
public void myTimeout(javax.ejb.Timer timer) { ... }
}
- Criando um Timer
. O próprio bean se registra para ouvir o timer
. O timer pode ser criado em Stateless e MessageDriven beans
. CUIDADO: não se deve criar timers em @PostConstruct nem @PreDestroy
(são chamados para cada
instância do pool), nem utilizando variáveis estáticas para verificar se
já foi criado (problema do cluster)
. O timer deve ser criado apenas através de métodos de negócio invocados
pelo cliente
. Um timer service pode ser obtido através do
EJBContext.getTimerService(), ou injetado pelo container:
@Resource
TimerService timerService;
. Interface TimerService
- public Timer createTimer(Date expiration, Serializable info)
. Ação única
. Expira na data especificada
- public Timer createTimer(long duration, Serializable info)
. Ação única
. Expira depois de passado o tempo especificado em
milisegundos
- public Timer createTimer(Date initialExpiration, long
intervalDuration, Serializable info)
. De intervalo
. Expira na data especificada e subsequentemente a cada
intervalo especificado em intervalDuration
- public Timer createTimer(long initialDuration, long
Página 32
33. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
intervalDuration, Serializable info)
. De intervalo
. Expira depois de passado o tempo initialDuration e
subsequentemente a cada intervalo
especificado em intervalDuration
- public Collection getTimers()
. Retorna uma coleção contendo todos os timers agendados
para o beans em questão (apenas os timers
do bean que chamou o método)
Obs.: Todos os métodos TimerService lançam as exceções:
. IllegalArgumentException: parâmetros negativos ou null
. IllegalStateException: chamado de onde não é permitido
. EJBException: encapsula qualquer outra exceção de
sistema
. Interface Timer
- public void cancel()
Cancela o timer
- public Serializable getInfo()
Recupera o objeto passado na criação do timer
- public Date getNextTimeout()
Recupera a data em que ocorrerá a próxima expiração
- public long getTimeRemaining()
Recupera o tempo restante para a próxima expiração
- public TimerHandle getHandle()
Recupera uma referência serializável à instância deste
timer
Obs.: Todos os métodos Timer lançam as exceções:
. NoSuchObjectLocalException: se invocado qualquer
método em um timer de ação única expirado ou um timer
cancelado
. EJBException: encapsula qualquer outra exceção de
sistema
. Reagendando temporizadores: precisa cancelar e criar um novo
. Transação
- É transacional no escopo da transação atual, ou seja, se
ocorrer rollback em um método que cria o timer,
sua criação será desfeita
- É boa prática o método de @Timeout ser transacional
RequiresNew, para assegurar que ele está no escopo
transacional do container
- Interceptors
. Criando Classes Interceptadoras
public class Profiler {
@AroundInvoke
public Object profile(InvocationContext invocation)
throws Exception {
long startTime = System.currentTimeMillis( );
try {
return invocation.proceed( );
} finally {
long endTime = System.currentTimeMillis(
) - startTime;
System.out.println("Method " +
Página 33
34. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
invocation.getMethod( ) + " took " + endTime + " (ms)");
}
}
}
. Interface InvocationContext
public interface InvocationContext {
public Object getTarget( );
public Method getMethod( );
public Object[] getParameters( );
public void setParameters(Object[] newArgs);
public java.util.Map<String, Object> getContextData( );
public Object proceed( ) throws Exception;
}
. getTarget - retorna uma referência à instância do bean
alvo
. getMethod - retorna um objeto java.lang.reflect.Method
do método que foi chamado
. getParameters - retorna os parâmetros que foram
enviados ao método
. setParameters - modifica tais parâmetros (usar com
cuidado)
. getContextData - retorna um objeto Map que fica ativo
durante toda a pilha de chamada, onde podemos
compartilhar valores com outros interceptors
. proceed - continua a pilha de chamada (próximo
interceptor ou método original)
. Aplicando Interceptors
- Nível de Classe (intercepta todos os métodos do bean)
@Stateless
@Interceptors(Profiler.class)
public class MyBean implements MyBeanRemote { ... }
- Nível de Método (intercepta apenas o método anotado)
@Stateless
public class MyBean implements MyBeanRemote {
@Interceptors(Profiler.class)
public void myMethod() { ... }
}
- Aplicando via XML
<ejb-jar>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>TravelAgentBean</ejb-name>
<!--
<exclude-default-interceptors> e <exclude-class-interceptors> -->
<interceptor-class>com.titan.Profiler</interceptor-class>
<method-name>bookPassage</method-name>
<method-params>
<method-param>com.titan.CreditCardDO</method-param>
<method-param>double</method-param>
</method-params>
Página 34
35. Resumo_Anotacoes_Certificacao_SCBCD_5.txt
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
. Obs:
- <method-name> é utilizado apenas para
aplicar a nivel de método
- <method-params> é útil se houver
overload no método especificado
- Interceptors Padrão
. Recebem um curinga (*). São aplicados para todos os
métodos de todos os beans do ejb-jar.
<ejb-jar>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>com.titan.Profiler</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
- Desativando Interceptors
. @ExcludeDefaultInterceptors
Ignora os interceptors-padrão da implantação
. @ExcludeClassInterceptors
Ignora os interceptors de nivel de classe do
bean
--------------------------------------------------------------------------------
------------------------------------------
- Interceptors e Injeção de Dependência (ENC)
. O interceptor compartilha o mesmo JNDI ENC do bean que está
interceptando
. O EJBContext também é compartilhado
. Injeção no Interceptor
- Via Annotations
public class AuditInterceptor {
@Resource
EJBContext ctx;
@PersistenceContext(unitName="auditdb")
EntityManager manager;
@AroundInvoke
public Object audit(InvocationContext
invocation) throws Exception { ... }
}
- Via XML
<ejb-jar>
<interceptors>
<interceptor>
<interceptor-class>com.titan.interceptors.AuditInterceptor</interceptor-class>
<around-invoke>
Página 35