Anotaciones en EJB 3.0

4
87782



Anotaciones
en EJB 3.0

Contenidos


Introducción


API de Persistencia: EntityManager



persistence.xml



Ciclo de vida de una Entidad


Uso de
anotaciones



   

Anotaciones
de un Bean de Sesión



   

Anotaciones
de un Bean de Entidad
   


   

Mé;todos del ciclo de vida de una Entidad


           
Mapeos
objeto-relacional



   

Anotaciones de un Bean Manejador de Mensajes



   

Anotaciones comunes a los Beans de Sesión y Beans
Manejadores de Mensajes

           
Transacciones

           
Interceptores

           
Métodos del ciclo de vida

           

Seguridad

           
Inyección de dependencias


Juntándolo
todo



Links de interés


Conclusión

 


Introducción

Aunque sigue habiendo
muchos escépticos (y no es para menos) parece que por fin tenemos una versión de
EJBs que nos aporta muchas cosas buenas sin hacernos la vida imposible. Con esta
nueva versión se ha intentado facilitar todas las partes que se complicaban en
versiones anteriores (descriptores de despliegue interminables, obligación de
implementar interfaces, obligación de implementar métodos que nunca se utilizan
…).

Son muchas las mejoras
que se han incluido en la versión 3.0 de los EJBs pero una de las más
importantes es la utilización de anotaciones. Mediante las anotaciones se define
la seguridad, transaccionalidad, persistencia, mapeo objeto-relacional, se
obtienen objetos mediante inyección, etc.


API de Persistencia: EntityManager



Primero veremos algunas partes importantes de la API, como la interfaz
EntityManager. Esta interfaz
es en la que se apoya la API de persistencia y la que se encarga del mapeo entre una
tabla relacional y su objeto Java. Funcionalmente es similar a la clase Session de Hibernate o a PersistenceManager de JDO. Proporciona
mé;todos para manejar la
persistencia de un Bean de Entidad, permite añadir, eliminar, actualizar y consultar así como manejar su ciclo de vida. Sus métodos má;s importantes son:


  • persist(Object entity)  – almacena el objeto
    entity en la base de datos.



  • merge(T entity)
     
    – actualiza las modificaciones en la entidad devolviendo la lista resultante.



  • remove(Object entity)
    – elima la entidad.



  • find(Class<T> entity, Object primaryKey) – busca la entidad a través de su clave primaria.



  • flush() – sincroniza las entidades con el contenido de la base de datos.



  • refresh(Object entity) – refresca el estado de la entidad con su contenido en la base de datos.



  • createQuery(String query) – Crea una query utilizando el lenguaje JPQL.



  • createNativeQuery() - Crea una query utilizando el lenguaje SQL.



  • isOpen() – Comprueba si está; abierto el EntityManager.



  • close() - Cierra el EntityManager.


Podemos obtener una referencia al EntityManager a través de la anotación @PersistenceContext.
El contenedor de EJB nos proporciona el contexto de persistencia
mediante inyección por lo que no tendremos que preocuparnos de
su creación y destrucción.

@PersistenceContext

EntityManager entityManager;

 


Otra forma de obtener un EntityManager es a través de la factoría
EntityManagerFactory


con el nombre del contexto de persistencia configurado en el persistence.xml .

EntityManagerFactory emf = Persistence.createEntityManagerFactory(«TestPersistence«);

EntityManager em = emf.createEntityManager();

En el método createEntityManagerFactory de la clase Persistence se debe pasar el nombre del contexto definido en el persistence.xml .





persistence.xml



Es
donde se definen los contextos de persistencia de la aplicación.
Se debe situar dentro del directorio META-INF.

<persistence>

<persistence-unit name=»TestPersistence«>

<jta-data-source>java:/AnotacionesEJB3DS</jta-data-source>

<properties>

<property name=»hibernate.dialect» value=»org.hibernate.dialect.MySQLDialect«/>

</properties>

</persistence-unit>

</persistence>

persistence-unit name:
Especifica un nombre
para el contexto persistente.

Si únicamente se especifica
uno, no habrá; que incluir su nombre cuando se recupere el EntityManager
(con la anotación @PersistenceContext o @PersistenceUnit).

jta-data-source, non-jta-data-source:
Nombre
JNDI del DataSource. También debemos crear un DataSource para la conexión a la base de datos:

<datasources>

<local-tx-datasource>

<jndi-name>AnotacionesEJB3DS</jndi-name>

<connection-url>jdbc:mysql://localhost:3306/AnotacionesEJB3DS</connection-url>

<driver-class>com.mysql.jdbc.Driver</driver-class>

<user-name>username</user-name>

<password>password</password>

</local-tx-datasource>

</datasources>

properties:
Se especifica
el tipo de base de datos a utilizar.



Ciclo de vida de una Entidad

Engloba dos aspectos: la relación
entre el objeto Entidad y su contexto a persistir y por otro lado la
sincronización de su estado con la base de datos. Para realizar
estas operaciones la Entidad puede encontrarse en cualquiera de estos
cuatro estados:



  • new
    Nueva instancia de la Entidad en memoria sin que aún le sea
    asignado su contexto persistente almacenado en la tabla de la base de
    datos.



  • managed 
    La Entidad dispone de contenido asociado con el de la tabla de la base
    de datos debido a que se utilizó el método
    persist().
    Los cambios que se produzcan en la Entidad se podrá;n sincronizar
    con los de la base de datos llamando al método
    flush().



  • detached – La Entidad se ha quedado sin su contenido persistente. Es necesario utilizar el método
    merge() para actualizarla.



  • removed – Estado después de llamarse al método
    remove() y el contenido de la Entidad será; eliminado de la base de datos.

Ciclo de Vida de una Entidad


Uso
de anotaciones


Una
anotación o metadato proporciona un recurso adicional al
elemento de código al que va asociado en el momento de su
compilación. Cuando la JVM ejecuta la clase busca estos
metadatos y determina el comportamiento a seguir con el
có;digo
al que va unido la anotación. 



Anotaciones
de un Bean de Sesión


@Stateful:
Indica que el Bean de Sesión es con estado.


  • name
    – por defecto el nombre de la clase pero se puede especificar otra
    diferente.


  • mappedName
    si se quiere que el contenedor maneje el objeto de manera
    específica. Si incluimos esta opción nuestra
    aplicación puede que no sea portable y no funcione en otro
    servidor de aplicaciones.


  • description
    descripción de la anotación.


@Stateless:
Indica que el Bean de Sesión es sin estado.


  • mismos atributos
    que @Stateful


@Init:
Especifica que el método se corresponde con un
método
create de un EJBHome o EJBLocalHome de EJB 2.1. Sólo se
podrá; llamar una única vez a este
método.


  • value
    – indica el nombre del correspondiente método create de la
    interfaz home adaptada. Sólo se debe utilizar cuando se
    utiliza
    el bean anotado con un bean con estado de la especificación
    2.1
    o anterior y que disponga de má;s de un método
    create


@Remove:
Indica que el contenedor debe llamar al método cuando quiera
destruir la instancia del Bean.


  • retainIfException
    indica si el Bean debe mantenerse activo si se produce una
    excepción. Por defecto a false.


@Local:
Indica que la interfaz es local.


@Remote:
Indica que la interfaz es remota.


@PostActivate:
Invocado después de que el Bean sea activado por el
contenedor.


@PrePassivate: Invocado antes de que el Bean
esté en estado passivate.



Anotaciones
de un Bean de Entidad


@Entity:
Indica que es un Bean de Entidad.


  • name – por defecto el nombre de la clase
    pero se puede especificar otra diferente.


Métodos del ciclo de vida de una Entidad


@EntityListeners:
Se pueden definir clases oyentes (listeners)
con métodos de
ciclo de vida de una entidad. Para hacer referencia a un
listener se debe incluir esta anotación
seguido entre
paréntesis de la clase: @Entity
Listeners(MyListener.class)

@ExcludeSuperclassListeners:
Indica que ningún listener de la
superclase será; invocado
por la entidad ni por ninguna de sus subclases.


@ExcludeDefaultListeners:
Indica que ningún listener por defecto
será;
invocado por esta clase ni por ninguna de sus subclases.


@PrePersist:
El método se llamará; justo antes de la
persistencia del
objeto. Podría ser necesario para asignarle la clave
primaria a la entidad a persistir
en base de datos.


@PostPersist:
El método se llamará; después de la
persistencia
del objeto.

@PreRemove:
El método se llamará; antes de que la

entidad sea eliminada.


@PostRemove:
El método se llamará; después de
eliminar la entidad de la base de datos.


@PreUpdate:
El método se llamará; antes de que una entidad sea
actualizada en base de datos.


@PostUpdate:
El método se llamará; después de que la
entidad sea actualizada.


@PostLoad:
El método se llamará; después de que
los campos
de la entidad sean cargados con los valores de su entidad
correspondiente de la base de datos. Se suele utilizar para
inicializar valores no persistidos.


@FlushMode:
Modo en que se ejecuta la transacción: FlushModeType.AUTO
(por defecto) y FlushModeType.COMMIT.


@NamedQuery:
Especifica el nombre del objeto query utilizado junto a EntityManager.


  • name
    – nombre del objeto query.


  • query – especifica
    la query a la base de datos mediante lenguaje Java Persistence Query
    Language
    (JPQL)


@NamedQueries:
Especifica varias queries como la anterior.


@NamedNativeQuery:
Especifica el nombre de una query SQL normal.


  • name
    – nombre del objeto query.


  • query – especifica
    la query a la base de datos.


  • resultClass – clase
    del objeto resultado de la ejecución de la query.


  • resultSetMapping
    nombre del SQLResultSetMapping
    definido (se explica má;s abajo).


@NamedNaviteQueries:
Especifica varias queries SQL.


@SQLResultSetMapping:
Permite recoger el resultado de una query SQL.


  • name
    – nombre del objeto asignado al mapeo.


  • EntityResult[] entities()
    entidades especificadas para el mapeo de los datos.


  • ColumnResult[] columns() – columnas de la tabla  para el mapeo
    de los datos.

@NamedNativeQuery(name=»nativeResult», query=»SELECT NOMBRE,APELLIDOS FROM USUARIOS WHERE
USUARIO_ID= 123″,
resultSetMapping = «usuarioNamedMapping«)

@SqlResultSetMapping(name=»usuarioNamedMapping«,

entities = {


@EntityResult(entityClass = com.autentia.entidades.Usuario.class,

fields = {

@ColumnResult(name=»usuarioId», column=»USUARIO_ID»),


@ColumnResult(name=»nombre», column=»NOMBRE»),



@ColumnResult(name=»apellidos», column=»APELLIDOS»)})})



@PersistenceContext:
Objeto de la clase EntityManager que nos proporciona todo lo que
necesitamos para manejar la persistencia.


  • name
    – nombre del objeto utilizado para la persistencia en caso de ser diferente al de la clase donde se incluye la anotación.


  • unitName – identifica la unidad de la persistencia usada en el bean en caso de que hubiera má;s de una.


  • type – tipo de persistencia, TRANSACTION (por defecto) | EXTENDED.


@PersistenceContexts:
Define varios contextos de persistencia.


@PersistenceUnit:
Indica la dependencia de una EntityManagerFactory definida en el
archivo persistence.xml.


  • name
    – nombre del objeto utilizado para la persistencia en caso de ser diferente al de la clase donde se incluye la anotación.


  • unitName – identifica la unidad de la persistencia usada en el bean en caso de que hubiera má;s de una.



Mapeos
objeto-relacional

Existen
varios tipos de relaciones entre tablas de una base de datos: 1:1, 1:N,
N:1 y N:M (en la implementación se considera lo mismo 0 que 1). Cuando
se establecen relaciones entre entidades (1:N, N:M), se utilizan
colecciones para mapear los campos de la entidad relacionada
(por ejemplo si un banco tiene N cuentas, en la entidad que mapeará; la
tabla banco se deberá; crear una colección de cuentas para almacenar los
valores de la relación. 

Las
relaciones entre entidades pueden ser unidireccionales o
bidireccionales. En las unidireccionales únicamente se puede acceder
desde una entidad a otra, no al revés. En las bidireccionales la
relación se da en los dos sentidos por lo que desde cualquiera de las
dos entidades se accede a la otra.

Hay que tener especial cuidado
en establecer los lados de una relación ya que existen dos: «owning
side» (lado propietario) e «inverse side» (lado inverso). El lado propietario
es el que tiene la clave forá;nea de la relación y el inverso el que la
proporciona. Únicamente las relaciones bidireccionales poseen los dos
lados. Si la relación es 1:N el lado propietario
es el lado que contiene la N. En las relaciones N:M ambos pueden tener
el lado propietario y en las relaciones 1:1 la entidad que contiene la
clave forá;nea es la que se considera que tiene el «owning side».


@Table:
Especifica la tabla principal relacionada con la entidad.


  • name
    – nombre de la tabla, por defecto el de la entidad si no se
    especifica.


  • catalog
    – nombre del catá;logo.


  • schema
    – nombre del esquema.


  • uniqueConstraints
    – constrains entre tablas relacionadas con la
    anotación @Column
    y @JoinColumn


@SecondaryTable:
Especifica una tabla secundaria relacionada con el Bean de entidad si
éste englobara a má;s de una. Tiene los mismos
atributos que
@Table


@SecondaryTables:
Indica otras tablas asociadas a la entidad.


@UniqueConstraints:
Especifica que una única restricción se incluya
para la
tabla principal y la secundaria.


@Column:
Especifica una columna de la tabla a mapear con un campo de la entidad.


  • name
    – nombre de la columna.


  • unique
    – si el campo tiene un único valor.


  • nullable
    – si permite nulos.


  • insertable
    – si la columna se incluirá; en la sentencia INSERT generada.


  • updatable
    – si la columna se incluirá; en la sentencia UPDATE generada.


  • table
    – nombre de la tabla que contiene la columna.


  • length
    – longitud de la columna.


  • precision
    – número de dígitos decimales.


  • scale
    – escala decimal.


@JoinColumn:
Especifica una campo de la tabla que es foreign key de otra tabla definiendo la relación del lado propietario.


  • name
    – nombre de la columna de la foreign key.


  • referenced
    – nombre de la columna referencia.


  • unique
    – si el campo tiene un único valor.


  • nullable
    – si permite nulos.


  • insertable
    – si la columna se incluirá; en la sentencia INSERT generada.


  • updatable
    – si la columna se incluirá; en la sentencia UPDATE generada.


  • table
    – nombre de la tabla que contiene la columna.


@JoinColumns:
Anotación para agrupar
varias JoinColumn.


@Id:
Indica la clave primaria de la tabla.


@GeneratedValue:
Asociado con la clave primaria, indica que ésta se debe
generar por ejemplo con una secuencia de la base de datos.


  • strategy
    – estrategia a seguir para la generación de la
    clave: AUTO
    (valor por defecto, el
    contenedor decide la estrategia en función de la base de datos),
    IDENTITY (utiliza un contador, ej: MySQL), SEQUENCE (utiliza una
    secuencia, ej: Oracle, PostgreSQL) y
    TABLE (utiliza una tabla de identificadores).


  • generator
    – forma en la que general la clave.

@SequenceGenerator:
Define
un generador de claves primarias utilizado junto con la
anotación

@GeneratedValue.
Se
debe especificar la secuencia en la entidad junto a la clave
primaria.

  • name
    – nombre del generador de la clave.


  • sequence
    – nombre de la secuencia de la base de datos del que se va a
    obtener la clave.

  • initialValue
    – valor inicial de la secuencia.


  • allocationSize
    – cantidad a incrementar de la secuencia cuando se llegue al
    má;ximo.


@TableGenerator:
Define una tabla de claves primarias generadas.
Se debe especificar en la
anotación
@GeneratedValue con
strategy = GenerationType.TABLE.


  • name
    – nombre de la secuencia.


  • table
    – nombre de la tabla que guarda los valores generados.


  • catalog
    – catalog de la tabla.


  • schema
    – esquema de la tabla.


  • pkColumn
    Name()
    – nombre de la clave primaria de la tabla.


  • valueColumn
    Name()
    – nombre de la columna que guarda el
    último valor
    generado.

  • pkColumn
    Value()
    – valor de la clave primaria.


  • initialValue
    – valor inicial de la secuencia.


  • allocationSize
    – cantidad a incrementar de la secuencia.

  • uniqueConstraints
    constrains entre tablas relacionadas.


@AttributeOverride:
Indica que sobrescriba el campo con el de la base de datos
asociado.


  • name
    – nombre del campo


  • column
    – columna del campo


@AttributeOverrides:
Mapeo de varios campos.


@EmbeddedId:
Se utiliza para formar la clave primaria con múltiples
campos.


@IdClass:
Se aplica en la clase entidad para especificar una
composición
de la clave primaria mapeada a varios campos o propiedades de la
entidad.


@Transient:
Indica que el campo no se debe persistir.

@Version:
Se utiliza a la hora de persistir la entidad en base de datos para identificar las entidades según su versión. Se
actualiza automá;ticamente cuando el objeto es mapeado en la
base de datos.


@Basic:
Mapeo por defecto para tipos bá;sicos: tipos primitivos,
wrappers de los tipos primitivos, String, BigInteger, BigDecimal,
Date, Calendar, Time, Timestamp, byte[], Byte[], char[], Character[],
enumerados y cualquier otra clase serializable.

  • fetch
    – determina la forma en que se cargan los datos (relaciones entre tablas):
    FetchType.LAZY
    (carga de la entidad únicamente cuando se utiliza), FetchType.EAGER
    (carga de todas las entidades relacionadas con ella).

    • optional – si permite que el campo sea nulo. Por defecto true.

En el ejemplo de arriba:

FechType.EAGER
– En el momento de acceder a la tabla ‘Empleado’ también se
consultan las tablas Cuenta y Contrato y las
entidades que a su vez contuvieran éstas ya que está;n
relacionadas

FechType.LAZY
– En el momento de acceder a la tabla Empleado únicamente se
carga su contenido y en el momento de necesitar alguno de los campos
idCuenta o idContrato, es en ese momento cuando se
accede a la tabla correspondiente.

@OneToOne:
(1:1) Indica que un campo está; en relación con otro
(ej: dentro de una empresa, un
empleado tiene un contrato de trabajo).


  • cascade
    – forma en que se deben actualizar los campos: ALL, PERSIST,
    MERGE, REMOVE y REFRESH.

  • fetch
    – determina la forma en que se cargan los datos: FetchType.LAZY
    (carga de la entidad únicamente cuando se utiliza), FetchType.EAGER
    (carga de todas las entidades relacionadas con ella).


  • optional
    – si la asociación es opcional.


  • mappedBy
    – el campo que posee la relación,
    únicamente se
    especifica en un lado de la relación.


@ManyToOne:
(N:1) Indica que un campo está; asociado con varios campos de otra
entidad (ej: las cuentas que posee un banco o los empleados que pertenecen a un departamento).


  • cascade,
    fetch
    y optional
    – igual que la anterior
    anotación.


@OneToMany:
(1:N) Asocia varios campos con uno (ej: varios departamentos de una
empresa).


  • cascade,
    fetch
    y optional
    – igual que la anterior
    anotación.


  • mappedBy
    – El campo que posee la relación. Es obligatorio
    que la
    relación sea unidireccional.


@ManyToMany:
(N:M) Asociación de varios campos con otros con multiplicidad muchos-a-muchos
(ej: relaciones entre empresas).

  • cascade, fetch y mappedBy igual que la anterior
    anotación.


@Lob:
Se utiliza junto con la anotación @Basic
para indicar que un campo se debe persistir como un campo de texto largo si
la base de datos soporta este tipo.


@Temporal:
Se utiliza junto con la anotación @Basic
para especificar que un campo fecha debe guardarse con el tipo
java.util.Date o java.util.Calendar. Si no se especificara ninguno de
estos por defecto se utiliza java.util.Timestamp.


@Enumerated:
Se utiliza junto con la anotación @Basic
e indica
que el
campo es un tipo enumerado (STRING), por defecto ORDINAL.


@JoinTable:
Se utiliza en el mapeo de una relación
ManyToMany o en una
relación unidireccional OneToMany.


  • name
    – nombre de la tabla join a donde enviar la foreing key.


  • catalog
    – catalog de la tabla.


  • schema
    – esquema de la tabla.


  • joinColumns
    – columna de la foreign key de la tabla join que referencia a
    la
    tabla primaria de la entidad que posee la asociación
    (sólo
    en un lado de la relación).


  • inverseJoinColumns
    – columnas de la foreign key de la tabla join que referencia
    a la
    tabla primaria de la entidad que no posee (lado inverso de la
    relación)


  • uniqueConstraints
    – constraints de la tabla.


@MapKey:
Especifica la clave de una clase de tipo java.util.Map


@OrderBy:
Indica el orden de los elementos de una colección por un
item
específico de forma ascendente o descendente.


@Inheritance:
Define la forma de herencia de una jerarquía de clases
entidad, es decir la relación entre las tablas relacionales
con los Beans de entidad.


  • strategy
    – SINGLE_TABLE que indica una única tabla para
    varias entidades (por defecto), JOINED, TABLED_PER_CLASS


@DiscriminatorColumn:
Relacionada con
@Inheritance
cuando
ésta define la estrategia SINGLE_TABLE. Define la columna
común de las entidades que forman la jerarquía..


  • name
    – nombre de la columna,
    por defecto DTYPE


  • DiscriminatorType
    – Tipo de la columna que se utiliza para identificar la
    entidad. Por defecto DiscriminatorType.STRING.

  • length
    – longitud de la columna, por defecto 31. 


@DiscriminatorValue:
Especifica el valor a persistir en la columna que identifica el tipo de
entidad a guardar en la tabla.


  • value
    – indica que el valor utilizado para diferenciar una clase de
    la
    jerarquía.

Un
ejemplo de esto se muestra en el siguiente diagrama. Se declaran tres
Beans de entidad para una única entidad de base de datos. A
la
hora de persistir la entidad tenemos que el campo TIPO de la tabla
MASCOTA hace referencia a varios tipos (PERRO, GATO).

Para resolver este problema se
utiliza la anotación @DiscriminatorColumn
que nos indica el campo que es común en las dos entidades
pero con diferente valor. Este valor se especifica en @DiscriminatorValue.

@PrimaryKeyJoinColumn:
Especifica la clave primaria de la columna que es clave extranjera de
otra entidad.

  • name
    – nombre de la clave primaria


  • referenced
    – nombre de la columna

@PrimaryKeyJoinColumns:
Agrupación de varias claves iguales que la anterior.

@Embeddable:
Con esta anotación podemos especificar en la propia entidad
un
objeto embebido de una entidad diferente.

@Embedded:
Especifica un campo persistente de una entidad especificado como
campo embebido de otra entidad mediante la anotación
@Embeddable.

@MappedSuperclass:
Asigna la información mapeada a la superclase de la entidad.



Anotaciones de un Bean Manejador de Mensajes


@MessageDriven:
Indica que la clase es un Bean Manejador de Mensajes.


  • name
    – 
    por defecto el nombre de la clase pero
    se puede especificar otra diferente.


  • messageListenerInterface
    – especifica la interfaz del Bean message listener.
     Obligatorio si el Bean no implementa una interfaz message
    listener o implementa má;s de una distinta a Serializable, Externalizable o
    alguna de las incluidas en el paquete javax.ejb.


  • activationConfig – otras opciones de configuración para JMS.  Equivalentes a las que se introducían en la etiqueta <activation-config-property> del descriptor de despliegue en EJB 2.1 (destinationType, messageSelector, acknowledgment, durability).


  • mappedName – si
    se quiere que el contenedor maneje el objeto indicado de manera
    específica.


  • description
    – descripción del Bean.



Anotaciones comunes a los Beans de Sesión y Beans
Manejadores de Mensajes


@Timeout:
Asigna un tiempo de ejecución a un método.

@ApplicationException:
Excepción a enviar al cliente cuando se produzca.



Transacciones

@TransactionManagment:
Especifica quién va a manejar la transacción: el
contenedor (TransactionManagmentType.CONTAINER) o el Bean (
TransactionManagmentType.BEAN).

@TransactionAttribute:
Especifica el método de negocio  invocado por el
contexto
de la transacción únicamente si es manejada por
el
contenedor. (TransactionAttributeType.REQUIRED  (por defecto)
|
MANDATORY | REQUIRES_NEW | SUPPORTS | NOT_SUPPORTED | NEVER.



Interceptores

Los Interceptores son
métodos invocados automá;ticamente al
invocar a los métodos de negocio de un Bean

@Interceptors: Interceptores asociados
con el Bean.  Entre paréntesis y llaves se indican
las clases donde se declaran. 

@AroundInvoke:
Designado a un método de un Interceptor.  Se
llamará;
a este método siempre que se llame a un método
anotado
con @Interceptors
({NombreInterceptor.class}) donde NombreInterceptor es la clase donde se declara el
método anotado con @AroundInvoke

@ExcludeDefaultInterceptors:
Se utiliza para excluir la invocación de un 
Interceptor en caso de que para un
Bean de negocio en concreto no queramos que se llame a
ningún metodo de los 
Interceptores.

@ExcludeClassInterceptors:
Excluye
la invocación de los 
Interceptores a nivel de clase.  



Métodos del ciclo de vida

@PostConstruct:
Invocado después de que el Bean sea creado
(después de hacerse las inyecciones de dependencias).

@PreDestroy:
Invocado después de que el Bean sea destruido del pool del
contenedor.


Seguridad


@DeclareRoles:
Especifica los roles de seguridad asociados con el Bean. 

@RolesAllowed: Sólo
los roles declarados en esta anotación podrá;n
invocar el
método. Se pueden especificar los roles permitidos
a nivel
de clase o a nivel de método. 

@PermitAll:
Permite la invocación por todos los roles definidos. Se
puede especificar a nivel de clase o a nivel de método.

@DenyAll: Bloquea
a todos los roles de manera que ninguno podrá; llamarlo.
Únicamente a nivel de método.

@RunAs: Se aplica
a nivel de clase e indica el rol con el que se ejecuta el Bean:
@RunAs(«admins»)



Inyección de dependencias

El
concepto de inyección de dependencias se refiere a la separación entre la
construcción de un objeto a la de su utilización. El contenedor
es el encargado de la creación y destrucción del objeto que
es devuelto a quien lo solicita.

@EJB:
Mediante esta anotación el contenedor asigna
rá; la referencia del
EJB indicado.  


  • name
    – nombre del recurso.


  • BeanIntefface
    – tipo de la interfaz de referencia, por defecto Object.class


  • BeanName
    – nombre del Bean especificado con el atributo name  en
    caso de que varios Beans implementen la misma interfaz .


  • mappedName
    –  
    si
    se quiere que el contenedor maneje el objeto indicado de manera
    específica.


  • description
    – descripción de la anotación para la
    inyección de dependencia.

@EJBs: Igual que
la anotación anterior pero especificando má;s de
un EJB.

@Resource:
Referencia de un recurso específico. 


  • name
    – nombre del recurso.


  • type
    tipo del objeto (Object.class por defecto)


  • authenticationType
    – especifica dónde se debe realizar el proceso de
    autenticación,
    por defecto AuthSenticationType.CONTAINER


  • shareable
    – indica si el objeto se comparte, por defecto true.


  • mappedName
    –  
    si
    se quiere que el contenedor maneje el objeto indicado de manera
    específica.


  • description
    – descripción de la anotación para la
    inyección de dependencia.

@Resources:
Referencia múltiples recursos en la misma
anotación.



Juntándolo todo


Bueno parece que todo esto está muy bien, parece fácil pero ¿realmente lo es?.
La verdad es que s&iacute, ahora programar EJBs es dé lo más sencillo e intuitivo.
Basta con crear unos cuantos POJOs a&ntildeadiéndoles determinadas anotaciones para
especificar lo que queremos hacer.

El
ejemplo lo seguiremos sobre la entidad Empleado que ve&iacuteamos en el diagrama
anterior. Mediante los beans podremos dar de alta/baja y listar los empleados.
Todo esto sin escribir una sola l&iacutenea de código SQL y sin complicarnos la vida.


EmpleadoEB



package
com.autentia.ejb3.bean.entity;

 


import
java.io.Serializable;


import
javax.persistence.Entity;


import
javax.persistence.GeneratedValue;


import
javax.persistence.GenerationType;


import
javax.persistence.Id;


import
javax.persistence.JoinColumn;


import
javax.persistence.OneToOne;


import
javax.persistence.Table;

 

/* Indica
que el Bean es de Entidad */

@Entity

/* Tabla a
mapear el bean EmpleadoEB, en este caso se mapeará a la tabla ‘empleado’
*/

@Table(name
=
«empleado»)

/* Los
Beans de Entidad deben implementar la interfaz Serializable */


public

class
EmpleadoEB
implements

Serializable {


    
private

static

final

long

serialVersionUID

= -4746762732969376696L;


    
private

int

idEmpleado;

    
private
String
nombre;

    
private
String
apellidos;

    
private
String
nif;

    
private
CuentaEB
cuenta;

    
private
ContratoEB
contrato;

 

         
/* Deben
tener un constructor sin argumentos */

    
public
EmpleadoEB() {

         
super
();

    

}

 

    
public
EmpleadoEB(int
idEmpleado, String nombre, String apellidos, String nif) {

         
super
();

         
this
.idEmpleado
= idEmpleado;

         
this
.nombre
= nombre;

         
this
.apellidos
= apellidos;

         
this
.nif
= nif;

    

}

 

         
/* Clave
primaria de la tabla ‘empleado’ */

    

@Id

    
/* Indica que la generación de la clave será automática (la aporta la
base de datos) */

    
@GeneratedValue(strategy
= GenerationType.
AUTO)

    
public

int
getIdEmpleado() {

         
return

idEmpleado
;

    

}

 

    
public

void
setIdEmpleado(int
idEmpleado) {
this.idEmpleado
= idEmpleado;

}

 

    
public
String getNombre() {
return

nombre
;

}

 

    
public

void
setNombre(String nombre) {
this.nombre
= nombre;

}

 

    
public
String getApellidos() {
return
apellidos;

}


    
public

void
setApellidos(String apellidos) {
this.apellidos
= apellidos;

}

 

    
public
String getNif() {
return

nif;

}

 

    
public

void
setNif(String nif) {
this
.nif
= nif;

}

 

         
/*
Multiplicidad 1:1 (1 empleado tiene 1 contrato y el campo es obligatorio
*/

    
@OneToOne(optional
=
false)

    

/* El contrato está en la tabla ‘contrato’ con la clave idContrato */

    
@JoinColumn(name=«idContrato»)

    
public
ContratoEB getContrato() {
return
contrato;

}

 

    
public

void
setContrato(ContratoEB contrato) {
this.contrato
= contrato;

}

 

         
/*
Multiplicidad 1:1 (1 empleado tiene 1 cuenta */

    
@OneToOne(optional
=
false)

    

/* La cuenta está en la tabla ‘cuenta’ con la clave idCuenta */

    
@JoinColumn(name=«idCuenta»)

    
public
CuentaEB getCuenta() {
return
cuenta;

}


    
public

void
setCuenta(CuentaEB cuenta) {
this
.cuenta
= cuenta;

}

}


Ahora creamos dos Beans de Sesión sin estado. El primero es el EmpleadoMgrBean
que hace de fachada al segundo, el EmpleadoDAOBean que contiene el contexto de
persistencia. Cada Bean de Sesión deberá implementar su interfaz local
correspondiente que declara los métodos utilizados en el Bean. Los interfaces
serán locales ya que no será necesario invocar a los EJBs desde una máquina
remota. Si se quisiera invocar al Bean desde remoto habrá que crear una interfaz
igual a la local y anotarla con
@Remote.


EmpleadoMgrLocal



package
com.autentia.ejb3.bean;

 


import
java.util.List;


import
javax.ejb.Local;


import
com.autentia.ejb3.bean.entity.EmpleadoEB;


import
com.autentia.ejb3.util.Empleado;

@Local
/*
Indica que la Interfaz es local */


public

interface
EmpleadoMgrLocal {

 

    
public

static

final
String
JNDIName
=
«EmpleadoMgrBean» +
«/local»;

    
public
List<EmpleadoEB> listarEmpleados();

    
public

boolean
altaEmpleado(Empleado
empleado);

    
public

boolean
eliminarEmpleado(Empleado
empleado);

}

 

 


EmpleadoMgrBean



package
com.autentia.ejb3.bean;


import
java.util.List;


import
javax.ejb.EJB;


import
javax.ejb.Stateless;


import
org.apache.log4j.Logger;


import
com.autentia.ejb3.bean.entity.BancoEB;


import
com.autentia.ejb3.bean.entity.ContratoEB;


import
com.autentia.ejb3.bean.entity.CuentaEB;


import
com.autentia.ejb3.bean.entity.EmpleadoEB;


import
com.autentia.ejb3.dao.EmpleadoDAOLocal;


import
com.autentia.ejb3.util.Empleado;

 

/* Indica
que el Bean de Sesión es sin estado */

@Stateless


public

class
EmpleadoMgrBean
implements

EmpleadoMgrLocal {

    
public

static
Logger
log
= Logger.getLogger(EmpleadoMgrBean.class);

         

    
/* Inyección del EJB EmpleadoDAOLocal */

    

@EJB

    
private
EmpleadoDAOLocal
empleadoDAO;

 

    
public
List<EmpleadoEB> listarEmpleados()
{

         
return

empleadoDAO
.buscarEmpleados();

    

}

         

    
public

boolean
altaEmpleado(Empleado empleado)
{

         
EmpleadoEB empleadoEB =
new
EmpleadoEB();

         


empleadoEB.setNombre(empleado.getNombre());

         


empleadoEB.setApellidos(empleado.getApellidos());

         


empleadoEB.setNif(empleado.getNif());

         
empleadoEB.setContrato(new
ContratoEB(empleado.getSalario(),empleado.getContrato()));

         
empleadoEB.setCuenta(new
CuentaEB(empleado.getCuenta(),
new
BancoEB(empleado.getBanco())));

         
empleadoDAO.nuevoEmpleado(empleado);

 

         
if
(empleado.getIdEmpleado() != 0) {

              
return

true
;

         
}

         
return

false
;

    

}

    
public

boolean
eliminarEmpleado(Empleado
empleado) {

         
return

empleadoDAO
.bajaEmpleado(empleado);

    

}

}

 


EmpleadoDAOLocal



package
com.autentia.ejb3.dao;

 


import
java.util.List;


import
javax.ejb.Local;


import
com.autentia.ejb3.bean.entity.EmpleadoEB;


import
com.autentia.ejb3.util.Empleado;

    

@Local
/*
Indica que la Interfaz es local */

    
public

interface
EmpleadoDAOLocal {

    
public
List<EmpleadoEB> buscarEmpleados();

    
public

void
nuevoEmpleado(Empleado empleado);

    
public

boolean
bajaEmpleado(Empleado
empleado);

}

 


EmpleadoDAOBean



package
com.autentia.ejb3.dao;

 


import
java.util.List;


import
javax.ejb.Stateless;


import
javax.persistence.EntityManager;


import

javax.persistence.PersistenceContext;


import
org.apache.log4j.Logger;


import
com.autentia.ejb3.bean.entity.EmpleadoEB;


import
com.autentia.ejb3.util.Empleado;

 

/* Indica
que el Bean de Sesión es sin estado */

/*
Implementa la interfaz local EmpleadoDAOLocal */

@Stateless


public

class
EmpleadoDAOBean
implements

EmpleadoDAOLocal {

    
public

static
Logger
log
= Logger.getLogger(EmpleadoDAOBean.class);

 

         
/* Pedimos
una referencia al EntityManager */

    


@PersistenceContext

    

EntityManager
entityManager
;

 

    
public
List<EmpleadoEB> buscarEmpleados()
{

         
List<EmpleadoEB> empleados =

                          
entityManager.createQuery(«from
Empleado»
).getResultList();

         
return
empleados;

    

}

 

    
public

void
nuevoEmpleado(Empleado empleado) {

         
log.info(«nuevoEmpleado
– persistimos el empleado «
+

                   
empleado.getNombre() +
«,»
+ empleado.getApellidos());

 

         
try
{

              
entityManager.persist(empleado);

         
} catch
(Exception e) {

              
log.error(«nuevoEmpleado
– Error «
+ e.getMessage());

              


e.printStackTrace();

         

}

    

}

 

    
public

boolean
bajaEmpleado(Empleado empleado)
{

         
try
{

              

//
Recuperamos el id del empleado buscándolo por nif

              
entityManager.createQuery(«from
Empleado as c where c.nif=:nif «
).

                            
setParameter(
«nif»,
empleado.getNif()).getSingleResult();

 

              

//
Eliminamos el empleado (se elimina buscando por clave primaria)

              
entityManager.remove(empleado);

              
return

true
;

         
} catch
(Exception e) {

              
log.error(«bajaEmpleado
– Se ha producido un error al eliminar la entidad»
);

         

}

         
return

false
;

    

}

}

Para que esto haga
algo habr&iacutea que crearse una aplicación de prueba e invocar por JNDI al EJB.


com.autentia.ejb3.bean.EmpleadoMgrLocal

empleadoMgr;


com.autentia.ejb3.util.Empleado

empleado
= new com.autentia.ejb3.util.Empleado();

 


empleado.setNombre(
«JUAN»);


empleado.setApellidos(
«ALONSO
RAMOS»
);


empleado.setNif(
«12345678A»);


empleado.setCuenta(
«1111-2222-33-4444444444»);


empleado.setBanco(
«MI
BANCO»
);


empleado.setContrato(
«INDEFINIDO»);

Context
ctx
=

null
;

 


try
{


    

ctx =


new
InitialContext();


    


empleadoMgr
=
ctx
.lookup(EmpleadoMgrLocal.JNDIName);

}
catch
(NamingException ne) {


    


log
.warn(«Error
– «
, ne);

}

 


boolean
resultado =
empleadoMgr.altaEmpleado(empleado);

Links de interés

http://java.sun.com/products/ejb/reference/techart/index.html



http://docs.jboss.org/ejb3/app-server/tutorial/


https://blogs.oracle.com/enterprisetechtips/ejb-30-compatibility-and-migration


Conclusión


Puede que la nueva versión de EJBs no sea del agrado de todos ya que existen
otras posibilidades que gozan de mayor aceptación hoy en día como Spring, pero
hay que reconocer que se ha hecho mucho esfuerzo por mejorar la API y merece la
pena tenerla en cuenta.


Desde Autentia creemos en la difusión del
conocimiento y el software libre, como aportación hemos liberado nuestra
herramienta de gestión de empresas. Puedes probarla desde

http://tntconcept.adictosaltrabajo.com
o descargártela desde
aquí

4 COMENTARIOS

  1. Hola Juan Alonso.

    Muy buen tutorial y excelente para empezar…

    Pero dime una cosa? a todo eso no le estaría faltando los archivos de configuración del jboss y del ejb mismo. Los archivos XML, por que yo traté de hacer tal cual explicaste, pero por lo que me doy cuenta todavia falta la parte de configuración.

    Saludos.

    http://www.marcosjara.com

  2. Hola, muy bueno, pero no logro hacer funcionar el ejemplo, puedes subir los ficheros .xml de configuración por favor?
    Saludos.

  3. Hola Juan Alfonso,

    Yo de nuevo y espero que esta vez tengas tiempo para responder.

    Trataré de exponer a continuación mi pregunta resumiendo el concepto de la implementación que deseo realizar.

    Bien,

    Tengo un proyecto EJB de Ventas de Artículos que está funcionando perfectamente y controla super bien la parte del inventario, pero lo que no controla es la

    parte del pago por las mercaderías, por ej.: si es al contado o se generan cuentas a pagar, etc., tampoco controla la facturación.

    Lo que deseo es incorporar dichas funcionalidades nuevas, formas de pago y facturación al EJB de sesión (lógica existente) del método que guarda la venta,

    sin tocar ún sólo código de la clase sesion ben original.

    Es decir, deseo incorporarle esas funcionalidades por aspectos (AOP).

    He leido mucho y he encontrado que la programación por Aspectos para EJBs se realizan mediante interceptores y que segun la teoría, ésta es realizada sin

    tocar el código original de sesión en mi caso (justo lo que necesito), sin embargo al buscar implementaciones, todas las que encontré necesitan que SI se

    modifique el código original, justamente para adicionar el annotation de @Interceptor arriba del método de la sesión donde se guarda la venta.

    Esto me serviría perfectamente si pudiera vincular el @Interceptor al método de la clase sesion bean desde fuera de él, es decir desde alguna otra ubicación.

    Por ejemplo que tal dentro del mismo interceptor que quiero crear.

    Además, qué pasa si sólo tengo el código binario de la clase Sesion Bean que posee el método que guarda la Venta venta y quiero implementar dicha

    funcionalidad?, existe alguna otra forma de adicionar dicho interceptor?, por algún otro lado? desde fuera del Sesion EJB mismo.

    Saludos.

  4. Super chevere tu aporte pero podrias indicar cuando usar la anotacion @EJBs, que uso tiene y bajo que escenarios seria correcto hacer uso de la misma, y podrias por favor profundizar en la diferencia entre @EJB y @EJBs por favor, gracias

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad