Transacciones en Java - JTA - Java Transaction API

Una transacción es una operación en la que se realizan una serie de operaciones de forma completa, o no se realiza ninguna.
Por ejemplo, si tenemos que hacer una transferencia de una cuenta a otra en un banco, las dos operaciones forman un conjunto. No sería lógico que quitásemos efectivo de una cuenta y, por alguna razón, no pudiéramos ingresar el dinero en la cuenta de destino, o se realizan las dos operaciones o ninguna.
Una vez iniciada una transacción, ningún cambio queda reflejado hasta que no se confirmen las operaciones ("commit"), o se anulen las mismas ("rollback").
El API de Java para manipular transacciones se conoce como JTA (Java Transaction Api).

Existen tres conceptos de transacciones que son importantes para el desarrollo de aplicaciones Java EE.
  • Atomicidad. Las operaciones son un conjunto completo, o todas se realizan bien o si falla alguna, fallan todas a la vez.
  • Locking and Isolation. Dicho término implica que solamente una transacción puede actualizar un dato al mismo tiempo.
  • Modelo. Cuantas transacciones pueden estar activas en un mismo thread (hilo) en particular en un momento dato.
Existen varios modelos de transacciones:
  • Anidado (Nested). Una transacción consta de varias sus transacciones que corren en paralelo todas juntas.
  • Encadenado (Chained). Una transacción consta de varias subtransacciones que corren en secuencia.
  • Plano (Flat): Una transacción no puede contener subtransacciones. La plataforma Java EE solamente soporta el modelo plano.
El servicio de transacciones distribuidas de la Plataforma J2EE involucra cinco participantes diferentes:
  • Transaction Manager (TM): Es el encargado de proveer los servicios para demarcación de transacciones, manejo de recursos transaccionales, sincronización y propagación de contexto transaccional.
  • Application Server: Implementa los servicios necesarios para proveer un entorno de ejecución para las aplicaciones. Entre estos servicios está el manejo del estado transaccional.
  • Resource Manager (RM): Ofrece a las aplicaciones acceso a recursos. Este acceso se ofrece a través de Resource Adapters. Este participa de transacciones implementado una interfaz para el manejo de recursos transaccionales, que controla la asociación a una transacción, la finalización de una transacción y la recuperación de una transacción.
  • Aplicación basada en componentes transaccionales: Esta aplicación se ejecuta sobre un servidor de aplicaciones, el cual le ofrece, entre otros servicios, control de transacciones, como por ejemplo, una aplicación basada en Enterprise Java Bean.
  • Communication Resource Manager: Encargado de la propagación del contexto transaccional permitiendo acceder a recursos transaccionales ubicados en otros dominios.
Existen dos formas de representar las transacciones sobre las cuales el sistema está sujeto:
  • Mediante programación. Existe un bean que controla la transacción. Esta forma se llama Bean-Managed Transaction (BMT).
  • De forma declarativa. El desarrollador solamente debe establecer de forma declarativa como quiere que el contenedor maneje las transacciones. Esta forma lleva el nombre de Container-Managed Transactions (CMT).
En la primera especificación de EJB se propusieron dos modelos de componentes, conocidos como Entity Bean y Session Bean. La especificación 2.0 introduce un nuevo modelo de componente llamado Message Driven Bean.  

Atributos de Transacciones

Los atributos de las transacciones se especifican mediante anotaciones u opcionalmente, en un deployment descriptor. Las anotaciones de los atributos son las siguientes:
Required  @TransactionAttribute(REQUIRED) Si un cliente está corriendo dentro una transacción e
invoca un método de un bean, el método se ejecuta dentro de la
misma transacción. 
RequiresNew  @TransactionAttribute(REQUIRES_NEW) 
Si un cliente está corriendo dentro de una transacción e invoca un
método, la transacción del cliente se suspende, se inicia una
nueva, la cual se termina al finalizar el método y se reinicia la
transacción del cliente.  
Mandatory  @TransactionAttribute(MANDATORY) Mandatory  @TransactionAttribute(MANDATORY) 
Si el cliente está corriendo dentro una transacción e invoca un método
de un bean, el método se ejecuta dentro de la misma transacción. 
NotSupported  @TransactionAttribute(NOT_SUPPORTED).
Si el cliente está corriendo dentro de una transacción e invoca un método, la transacción del cliente se suspende, cuando termina el método, la transacción del cliente se reanuda.
Si el cliente no está asociado a una transacción, el contenedor no inicia una nueva.
Supports  @TransactionAttribute(SUPPORTS) 
Si el cliente está corriendo dentro una transacción e invoca un método de un bean, el método se ejecuta dentro de la misma transacción.
Si el cliente no está asociado con una transacción, el contenedor no inicia una nueva.
Never  @TransactionAttribute(NEVER) 
 
Ejemplo de código de una transacción:
 
@TransactionAttribute(NOT_SUPPORTED)
@Stateful
public class BeanTransacciones implements Transaction {
...
@TransactionAttribute(REQUIRES_NEW) @TransactionAttribute(REQUIRES_NEW)
public void PrimerMetodo() {...}
@TransactionAttribute(REQUIRED)
public void SegundoMetodo() {...}
public void TercerMetodo() {...}
public void CuartoMetodo() {...}
}
 
Para delimitar las transacciones JTA, se utilizan los siguientes métodos de la interfaz javax.transaction.UserTransaction:
  • begin
  • commit
  • rollback
Ejemplo:
import java.util.*;
import javax.ejb.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.transaction.*; 
@Stateless
@TransactionManagement(BEAN)
public class BeanStock implements Stock {
@Resource javax.Transaction.UserTransaction mitransaccion;
@Resource javax.sql.DataSource ds1;
public void updateStock(int idstock, double precio) {
Connection con = null;
PreparedStatement prepStmt = null;
try {
con = ds1.getConnection();
mitransaccion.begin();
prepStmt = con.prepareStatement(
"UPDATE Stock set precio = ? where IdStock = ?"); 
prepStmt.setDouble(1, precio);
prepStmt.setInt(2, idstock);
int registrosafectados = prepStmt.executeUpdate();
mitransaccion.commit();
} catch (Exception ex) {
try {
mitransaccion.rollback();
} catch (SystemException ex) {
throw new EJBException ("Rollback ha fallado: " + ex.getMessage();
}
throw new EJBException("La transaccion ha fallado: " + ex.getMessage());
} finally {
try {
prepStmt.close();
con.close();
} catch (SQLException ex) {
throw new EJBException ("Error al cerrar la conexion: " + e.getMessage()); 
}
}
}

Lenguaje JPQL

JPQL es un lenguaje que permite realizar consultas como operaciones masivas sobre Entity Beans. Presenta una sintaxis similar a SQL, pero no está orientado a un modelo relacional (basado en tablas y columnas) sino a un modelo de objetos (objetos y atributos).
Las Entidades son sensibles a mayúsculas y minúsculas en los nombres de la Entidad y los campos.

Ejemplo de una Entidad Empleado.
Si queremos recuperar todos los datos de un empleado:
 
SELECT e FROM Empleado e
 
La cláusula from no se refiere a una tabla, sino a una clase, en nuestro ejemplo, la clase Empleado.
El resultado de la consulta es un Entity Bean.
Para ejecutar esta consulta:
 
Query consulta = entityManager.createQuery("SELECT e FROM Empleado e");
List<Empleado> empleados= consulta.getListResult();
 
También se utilizan las anotaciones @NamedQuery y @NamedQueries. Para definir una única consulta en la clase de un Entity Bean:
 
@Entity
@Table(name = "EMPLEADOS")
@NamedQuery(name="todosempleados", query="SELECT e FROM Empleado e")
public class Empleado {
 //Métodos y elementos de la clase Empleado
}
 
Para definir más de una consulta dentro de un entity Bean se utiliza la anotación @NamedQueries que contiene un conjunto de consultas.
 
@Entity
@Table(name = "Empleado")
@NamedQueries(value={
 @NamedQuery(name=" todosempleados", query="SELECT e FROM Empleado e")
, @NamedQuery(name=" todosempleadosorden", query="SELECT e FROM Empleado e ORDER BY e.Nombre")}
)
public class Empleado {
//Métodos y elementos de la clase Empleado
}
 
Para ejecutarlas:
 
Query consulta = entityManager.createNamedQuery("todosempleados");
List<Empleado> empleados = consulta.getResultList();
 
Para utilizar parámetros posicionales, el parámetro debe indicarse en la cláusula where de la consulta con el prefijo ?, y a continuación el número posicional del parámetro.
 
SELECT e FROM Empleado e WHERE e.Nombre = ?1
 
Luego, al ejecutarla, se establece el valor del parámetro según su posición:
 
Query consulta = entityManager.createQuery("SELECT e FROM Empleado e WHERE e.Nombre = ?1");
consulta.setParameter(1, "SHUM");
Empleado emp = consulta.getSingleResult();
 
Para utilizar parámetros por nombre, el parámetro debe indicarse en la cláusula where de la consulta con el prefijo: y a continuación el nombre del parámetro:
 
SELECT e FROM Empleado e WHERE e.Nombre = :nombreempleado
 
Posteriormente, al ejecutarla, se establece el valor del parámetro según su nombre:
 
Query consulta = entityManager.createQuery("SELECT e FROM Empleado e WHERE e.Nombre = :nombreempleado");
consulta.setParameter("nombreempleado", "SHUM");
Empleado emp = consulta.getSingleResult();
 

Persistence en Java 2 - Anotaciones POJOs - Java Persistence API

Una entidad JPA (Java Persistence API)  no es más que un POJO que dispone de una serie de anotaciones, que indican qué y cómo se persiste un objeto, es decir, anotaciones para manipular objetos en una base de datos. Estas anotaciones vinculan cada propiedad del POJO con las de la base de datos y establecen un vínculo directo. 

Resumen de Anotaciones principales:

@Entity indica que el objeto es una entidad.
@Table indica la tabla de base de datos que contiene objetos.
@Id, @IdClass, @EmbeddedId se emplean para declarar claves primarias de clases
@Transient hace que una variable no sea tomada por una base de datos.
@Column indica la columna de una @Table
@Basic indica cuando y como se debe cargar un objeto persistido
@OneToOne es usada para crear relaciones entre otras entidades que tengamos de una a una
@OneToMany hace referencia a la asociación de 1-N.
@ManyToMany hace referencia a la asociación N-N.


Anotación @Entity indica que el objeto es una entidad.
La anotación @Table indica la tabla de base de datos que contiene objetos.
Ejemplo de código:

 
@Entity 
@Table(name="PRODUCTOS") 
public class Producto { 
     @Id 
    private String nombreproducto;  
    public Producto() { } 
     //METODOS Getters y Setters  
}
 
Toda entidad debe tener una clave primaria que se debe identificar con la anotación @Id. Además, todas las entidades deben contener un constructor vacío.

Anotaciones @Id, @IdClass, @EmbeddedId se emplean para declarar claves primarias de clases, bien de forma simple @Id o compuesta @IdClass y @EmbeddedId.
En el caso de las claves compuestas se debe definir una clase para representar a la clave primaria, y redefinir el método equals para comprobar que dos instancias de una clase sean iguales.
Veamos un código utilizando @IdClass

 
@Entity 
@IdClass(PersonaId.class) 
public class Persona { 
    @Id 
    private int id; 
    @Id 
    private String nombre;  
    ... 
    public Persona() { } 
    //METODOS Getters y Setters  
} 
 
public class PersonaId { 
     int id; 
    String nombre; 
     public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    } 
}
 
Y ahora un código utilizando @EmbeddedId

 
@Entity 
public class Persona { 
  @EmbeddedId 
  @AttributeOverrides({ 
     @AttributeOverride(name = "id", column = @Column(name = "ID",  
                              nullable = false, precision = 5, scale = 0)), 
    @AttributeOverride(name = "nombre", column = @Column(name = "NOMBRE"
                       nullable = false, length = 50)), 
 }) 
    private PersonaId id; 
    public Persona() { } 
    //Métodos Getters y Setters  
} 

@Embedded 
public class PersonaId { 
     int id; 
    String nombre; 
     public PersonaId() { } 
     public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    } 
} 
 

La anotación @Transient hace que una variable no sea tomada como un campo del origen de la base de datos.
Además tenemos diferentes formas de mapear cada campo indicando nombres de columna y otros valores.

 
@Entity 
@Table(name="PRODUCTOS") 
public class Productos { 
    @Id 
    private String nombreproducto;  
    @Column(name="CATEGORIA", nullable=false) 
    private String categoria; 
    @Column(name="DESCRIPCION", nullable=false) 
    private String descripcion; 
    @Lob 
    @Basic(fetch=FetchType.LAZY) 
    private byte[] imagen; 
    public Productos() { } 
    //Métodos Getters y Setters  
} 
 
La anotación @Column permite definir varias propiedades:
La propiedad name puede especificar el nombre de la columna
La propiedad nullable indica si la columna acepta valores null o no, si no se incluye el valor por defecto es true.
La anotación @Basic sirve para marcar cualquiera de los tipos de datos que son persistidos de forma automática además de las clases que sean Serializables.
La propiedad fetch permite definir cuándo se debe cargar el campo en cuestión.
FetchType.LAZY indica que el campo se va a cargar de forma "perezosa", es decir, el campo se cargará sólo cuando se acceda a su información, esto supone una consulta más para la obtención del campo, pero en casos de gran cantidad de información es muy beneficioso.
El valor FetchType.EAGER indica que el valor será cargado cuando se cargue el resto del objeto.
El valor por defecto de la propiedad fetch es FetchType.EAGER.
La anotación @Lob indica que el contenido de un campo básico será guardado como LOB (Large Object), por ejemplo, utilizamos esta anotación para marcar un campo que contenga un String o caracteres, el framework lo mapeará a una columna CLOB (Character Large Object).
Si es de otro tipo, normalmente byte[], será mapeado a una columna de tipo BLOB (Binary Large Object).
Anotación @Embeddable forman parte del objeto entidad al que pertenecen.

 
@Entity 
@Table(name="PERSONAS") 
public class Persona { 
    @Id 
    private String nombre;  
    @Embedded 
    @AttributeOverrides({@AttributeOverride(name="codigoPostal",  
                                            column=@Column(name="CODIGOPOSTAL")), 
                         @AttributeOverride(name="direccionPostal",  
                                            column=@Column(name="DIRECCIONPOSTAL")) 
    }) 
    private Direccion direccion; 
    public Persona() { } 
    //Métodos Getters y Setters  
} 

@Embeddable  
public class Direccion implements Serializable {  
    private String direccionPostal; 
    private String ciudad; 
    private int codigoPostal; 
    private String pais; 
    public Direccion() { } 
    public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    }   
}
 
Anotación @OneToOne es usada para crear relaciones entre otras entidades que tengamos declaradas:

 
@Entity 
@Table(name="PRODUCTOS") 
public class Producto { 
    @Id 
    private String nombreproducto;  
    @Column(name="DESCRIPCION", nullable=false) 
    private String descripcion; 
    @OneToOne 
    @JoinColumn(name="CATEGORIA _ID", 
        referencedColumnName="CATEGORIA_ID", updatable=false) 
    private Categoria categoria; 
    public Producto() { } 
    //Métodos Getters y Setters  
}  
@Entity 
@Table(name="CATEGORIAS") 
public class Categoria { 
  @Id 
  @Column(name=" CATEGORIA_ID") 
  private int id; 
  @Column(name="NOMBRECATEGORIA", nullable=false) 
  private String nombrecategoria; 
  public Categoria() { } 
  //Métodos Getters y Setters  
}
 
Anotación @OneToMany hace referencia a la asociación de 1-N en la que una entidad tiene dos o más referencias de otra.
Poniendo un ejemplo, podemos tener una tabla de jugador y otra de equipo. Un equipo puede contener varios jugadores, pero un jugador sólo puede estar en un equipo.
Esta relación sería:

 
@Entity 
@Table(name="EQUIPOS") 
public class Equipo { 
    ... 
    private Set<Jugador> jugadores = new HashSet<Jugador>(0); 
    //Métodos Getters y Setters  
   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,  
                                   mappedBy = "equipo") 
 public Set<Jugador> getJugadores() { return this.jugadores; } 
 public void setJugadores(Set<Jugador> jugador) { this.jugadores = jugador; }
 ... 
}  
 
La anotación @OneToMany indica que un equipo puede contener varios jugadores.
La anotación @OneToMany tiene una propiedad llamada cascade que define el tipo de operaciones se realizarán cuando se efectúen modificaciones sobre los datos de la entidad.
La propiedad Cascade, puede tomar los siguientes valores:

  • CascadeType.PERSIST: Cuando persistimos una entidad, todas las entidades que contenga esta variable serán persistidas también.
  • CascadeType.REMOVE: Cuando eliminemos una entidad, todas las entidades que contenga esta variable se borrarán del mismo modo.
  • CascadeType.REFRESH: Cuando actualicemos la entidad, todas las entidades que contenga esta variable se actualizarán.
  • CascadeType.MERGE: Cuando hagamos un "merge" de la entidad, todas las entidades que contenga esta variable realizarán la misma operación.
  • CascadeType.ALL: Todas las operaciones citadas anteriormente.
La propiedad mappedBy = "equipo" indica el nombre de la entidad Equipo en el objeto Jugador.
Anotación @ManyToMany hace referencia a la asociación N-N.
Volviendo al ejemplo de equipos y jugadores, la anotación @ManyToOne indicaría que varios jugadores pueden haber sido inscritos en diferentes equipos.
Anotaciones del ciclo de vida:

  • @EntityListeners: Se pueden definir clases listeners con métodos de ciclo de vida de una entidad.
Para hacer referencia a un listener, se debe incluir esta anotación seguido de 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 un EntityManager.
Sus atributos son:

    • name - nombre del objeto query.
    • query - especifica la query a la base de datos mediante lenguaje Java Persistence Query Language (JPQL)
  • @NamedQueries: Específica varias queries como la anterior.

Búsquedas de datos en Entidades

Una vez que tenemos las entidades, podemos realizar consultas de acción sobre los objetos utilizando las clases POJO's.
La forma más sencilla de buscar en un Entity Bean es utilizar el método find(Class, Object) de la interfaz EntityManager.
Este método permite buscar un Entity por su clave primaria.
Para buscar un producto, por ejemplo, podríamos usar el siguiente código:
Producto product = entityManager.find(Producto.class, new Long(1));
El primer parámetro del método find es la clase del Entity que deseamos buscar.
El segundo parámetro, la clave primaria del Entity.
La clase que pasemos como parámetro debe ser anotada con @Entity.

Búsquedas con JPQL

JPA ofrece una API para realizar tanto consultas como operaciones masivas sobre Entity Beans (actualizaciones y eliminaciones).
Esta API involucra tres componentes:

  • Métodos de consulta del EntityManager.
  • La interfaz javax.persistence.Query.
  • El lenguaje JPQL (Java Persistence Query Language).

Conexion a una Base de Datos mediante un EJB

1.Creamos una Enterprise Aplication con modulo EJB y WAR
2.Sobre la libreria EJB agregamos la libreria JAR de Oracle para el acceso a la base de datos
3.Sobre el proyecto EJB boton derecho y creamos un SessionBean indicando que es Remote y Stateless
3.Nos vamos a la carpeta EnterpriseBeans y en la sesion bean creada pulsamos sobre Add y sobre BusinessMethod
  y lo llamamos getEmpleadosDepartamento con ReturnType java.lang.String y en Parameters recibe un
  departamento que sera un int
4.La clase nos quedara de la siguiente manera para que vuelva a ser recuperada:
@Stateless
public class SessionBeanBuscadorEmpleados implements SessionBeanBuscadorEmpleadosRemote {
    @Override
    public String getEmpleadosDepartamento(int departamento) {
        try{
            //Agregamos la variable tabla donde se veran todos los elementos a buscar
            String tabla = "";
            //Creamos los accesos a la base de datos
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
            Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE","system", "123456");
            PreparedStatement st = conn.prepareStatement("SELECT NOMBRE, APELLIDOS, ID FROM EMPLEADOS WHERE ID="+departamento+"%");
            st.setInt(1, departamento);
            ResultSet rs = st.executeQuery();
            tabla = "<table border='1'>";
            while(rs.next()){
                tabla += "<tr>";
                tabla += "<td>"+rs.getString(1)+"</td>";
                tabla += "<td>"+rs.getString(2)+"</td>";
                tabla += "<td>"+rs.getString(3)+"</td>";
                tabla += "</tr>";
            }
            tabla += "</table>";
            return tabla;
        } catch (SQLException ex) {
            return "Error "+ex.toString();
        }
    }
}
5.Nos vamos al proyecto WAR y creamos un Servlet que llamara al EJB llamado ServletBuscadorEmpleados
6.Pulsamos boton derecho sobre el codigo del Servlet dentro del try y hacemos una llamada al EJB
  mediante InsertCode >> Call Enterprise Bean y seleccionamos nuestro Session bean dentro del EJB
7.El Servlet nos quedará:
@WebServlet(name = "ServletBuscadorEmpleados", urlPatterns = {"/ServletBuscadorEmpleados"})
public class ServletBuscadorEmpleados extends HttpServlet {
    @EJB
    private SessionBeanBuscadorEmpleadosRemote sessionBeanBuscadorEmpleados;
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            /* TODO output your page here. You may use following sample code. */
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet ServletBuscadorEmpleados</title>");           
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Tabla de Empleados:</h1>");
            int numero = Integer.parseInt(request.getParameter("txtnumerodep"));
            String tabla = sessionBeanBuscadorEmpleados.getEmpleadosDepartamento(numero);
            out.println(tabla);
            out.println("</body>");

Llamada de un EJB desde un Servlet con un JSP

1.Crearemos un proyecto EJB llamado ProyectoBeanSession marcando la opcion de tipo WAR y EJB
2.Sobre el proyecto EJB crearemos una nueva Session Bean y dejaremos el nombre por defecto NewSessionBean
  y lo incluiremos en un paquete llamado paquetebean.
3.Marcaremos la opción de tipo Session Stateful e indicaremos que será de tipo remoto y
  seleccionaremos el nombre del proyecto EJB.
4.Una vez creado el Bean, vamos a implementar un método de acción. Debemos ir al proyecto EJB, sobre la carpeta
  Enterprise Bean y seleccionamos la opción Add, Bussines Method para crearnos un método de negocio
5.Escribimos el nombre del método como getSaludo, e indicamos que el tipo que devolverá es de la clase String.
6.Nos quedará de la siguiente forma:
package paquetebean;
import javax.ejb.Stateless;
@Stateless
public class NewSessionBean implements NewSessionBeanRemote {
    @Override
    public String getSaludo() {
        return "Mensaje enviado desde un EJB @Stateless";
    }
}
7.Ahora vamos a agregar un Servlet que implementará la funcionalidad del componente EJB que nos hemos creado.
8.Sobre el proyecto WAR, seleccionaremos la opción New >> Servlet y lo llamaremos ServletLlamadaEJB
  y lo incluiremos en un paquete llamado paqueteservlets.
9.No incluiremos la información en el archivo descriptor de la aplicación, aunque si lo hiciéramos,
  la llamada al EJB seguiría siendo la misma.
10.Una vez que nos ha creado el servlet, debemos implementar la llamada, para ello utilizaremos el asistente
  de código que nos permite insertar código generado dinámicamente.
11.Sobre el código del servlet, seleccionamos la opción Insert Code
12.Nos aparecerán una serie de opciones, nosotros seleccionaremos la opción que llama a un Bean.
  Marcaremos la opción Call Enterprise Bean.
13.Al seleccionar dicha opción, nos muestra una ventana dónde debemos indicar el Bean que vamos a llamar
  desde el servlet. Buscamos el bean en el proyecto EJB y pulsamos sobre OK.
14.Podremos comprobar en el código que nos genera las librerías necesarias para realizar la llamada al
  Bean de Session:
    import javax.ejb.EJB;
    import paquetebean.NewSessionBeanRemote;
15.También nos generará un objeto para poder invocar al método getSaludo desde nuestro servlet. Dicho objeto
  podremos comprobar que contiene la anotación @EJB.
    @EJB
    private NewSessionBeanRemote newSessionBean;
16.Ahora vamos a implementar el servlet para poder escribir el saludo escrito y enviado desde el EJB:
@WebServlet(name="ServletLlamadaEJB", urlPatterns={"/ServletLlamadaEJB"})
public class ServletLlamadaEJB extends HttpServlet {
    @EJB
    private NewSessionBeanRemote newSessionBean;
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("<HTML>");
            out.println("<head>");
            out.println("<title>Servlet ServletLlamadaEJB</title>"); 
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Servlet con llamada a EJB</h1>");
            out.println(newSessionBean.getSaludo());
            out.println("</body>");
            out.println("</HTML>");
        } finally {
            out.close();
        }
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }
}
17.Nos quedaría realizar la llamada al Servlet. Para ello, sobre el proyecto WAR vamos a seleccionar la página
 que trae por defecto index.html y la implementaremos.Escribiremos el siguiente código:
<%@page contentType="text/HTML" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
    <head>
        <meta http-equiv="Content-Type" content="text/HTML; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Bienvenido a la prueba de EJB</h1>
        <form name="form1" action="ServletLlamadaEJB">
            <input type="submit" value="Pulse para llamar al componente EJB">
        </form>
    </body>
</HTML>

Llamada de un EJB desde un Servlet

1.Creamos un nuevo proyecto EnterpriseAplication con el modulo EJB y WAR con GlassFishServer
2.Sobre el proyecto EJB boton derecho y creamos un SessionBean indicando que es Remote usando nuestro mismo
  proyecto EJB por si queremos usarlo en otros proyectos, con sesion Stateless(sin estado)
3.Nos vamos a la carpeta EnterpriseBeans y en la sesion bean creada pulsamos sobre Add y sobre BusinessMethod
  y lo llamamos getDoble con ReturnType java.lang.String y en Parameters agregamos un numero de tipo int
4.La clase nos quedara de la siguiente manera para que vuelva a ser recuperada:
@Stateless
public class NewSessionBean implements NewSessionBeanRemote {
    @Override
    public String getDoble(int numero) {
        int resultado = numero*2;
        return "El doble del numero es "+resultado;
    }
}
5.Nos vamos al proyecto WAR, que es el que usuario vería y el que consumiría los EJB
6.Creamos un Servlet que llamara al EJB creado dentro de la carpeta Source Packages en el WAR
7.Pulsamos boton derecho sobre el codigo del Servlet dentro del try y hacemos una llamada al EJB
  mediante InsertCode >> Call Enterprise Bean y seleccionamos nuestro Session bean dentro del EJB lo que
  nos introducirá el codigo
  @EJB
  private NewSessionBeanRemote newSessionBean;
8.En el Servlet introduciremos una variable num que recogerá el valor que venga de un futuro JSP que creemos
  int num = Integer.parseInt(request.getParameter("txtnumero"));
9.Queremos utilizar este numero para llevarlo a nuestro Bean y calcular el doble:
  String texto = newSessionBean.getDoble(num);
  out.println(texto);
10.Creamos la pagina HTML donde introduciremos el numero a calcular el doble:
    <form name="form1" action="NewServletLlamada">
            Introducir un numero: <input type="text" name="txtnumero"><br>
            <input type="submit" name="Calcular Doble">
        </form>
11.El Servlet nos quedará:
@WebServlet(name = "NewServletLlamada", urlPatterns = {"/NewServletLlamada"})
public class NewServletLlamada extends HttpServlet {
    @EJB
    private NewSessionBeanRemote newSessionBean;
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            /* TODO output your page here. You may use following sample code. */
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet NewServletLlamada</title>");           
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Servlet NewServletLlamada at " + request.getContextPath() + "</h1>");
            int num = Integer.parseInt(request.getParameter("txtnumero"));
            String texto = newSessionBean.getDoble(num);
            out.println(texto);
            out.println("</body>");
            out.println("</html>");
        }
    }

Acceso de un Java Beans a una Base de Datos Oracle mediante un JSP

Enterprise Java Beans (EJB) es una plataforma para construir aplicaciones de negocio portables, reutilizables y escalables usando el lenguaje de programación Java.
Todo EJB está compuesto por dos capas:
- Capa Interfaz, indica a las clases que consuman los métodos y características del EJB.
- Capa Implementación es una clase que implementa la capa de Interfaz.


EJEMPLO:
 
1.Dibujaremos un control HTML Select que mantendrá el elemento seleccionado cuando enviemos una petición al
  servidor.
2.Creaamos un nuevo proyecto Web Application llamado ProyectoJSPJavaBeans y utilizaremos el servidor GlassFish
3.Sobre las librerías vamos a agregar el conector JAR para Oracle
4.Creamos una clase Java que será la que dibujará los elementos en el Select y los mantendrá seleccionados
  y la llamaremos BeanDesplegable y crearemos un paquete llamado paquetebeans:
package paquetebeans;
import java.sql.*;
public class BeanDesplegable {
    Connection cn = null;
    java.sql.Statement st = null;
    final String usuario = "system";
    final String password = "12345";
    final String cadenaconexion = "jdbc:oracle:thin:@localhost:1521:XE";
    public BeanDesplegable () {

    }
    public void conectar() throws SQLException, Exception
    {
        try
            {
                 // 1.- Cargamos el driver
                 DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
                // 2.- Nos conectamos, paso el nombre de la conexion, usuario y contraseña.
                this.cn = DriverManager.getConnection(cadenaconexion, usuario, password);
            } catch (SQLException e)
            {
                System.out.println(e.toString());
            }
    }

    public ResultSet crearResultSet(String consulta) throws Exception
    {
        ResultSet rs = null;
        st = cn.createStatement();
        rs = st.executeQuery(consulta);
        return rs;
    }

    public String cargarCombo(ResultSet rs, String nombreselect, String valorseleccionado)
    {
        String combo;
        try
        {
            combo = "<select name='"+nombreselect+"'>";
            while (rs.next())
            {
                if (rs.getString(1).equals(valorseleccionado))
                {
                    combo+="<option SELECTED ";
                }else{
                    combo+="<option ";
                }
                combo+="value='"+rs.getString(1)+"'>";
                combo+=rs.getString(2);
                combo+="</option>";
            }
            combo += "</select>";
            return combo;
        } catch (SQLException ex) {
            return ex.toString();
        }
    }
}
5.Utilizamos la página index.jsp del proyecto para poder realizar la llamada al Bean y mostrar los datos de
  los departamentos.
<%@page import="java.sql.ResultSet"%>
<%@page contentType="text/HTML" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<jsp:useBean id="practica" scope="session" class="paquetebeans.BeanDesplegable "/>
<HTML>
    <head>
        <meta http-equiv="Content-Type" content="text/HTML; charset=UTF-8">
        <title>Cargar Desplegable</title>
    </head>
    <body>
        <form name="form1" action="index.jsp" method="post">
            Seleccione un departamento:
          <%
          practica.conectar();
          ResultSet rs =practica.crearResultSet("SELECT DEPTNO,DNAME FROM DEPT");
          String combo;
          String etiqueta="";
          if (request.getParameter("cmbdept")!=null)
          {
                String dept = request.getParameter("cmbdept");
                combo = practica.cargarCombo(rs, "cmbdept", dept);
                etiqueta = "<div>Elemento seleccionado:"+dept+"</div>";
          }else{
                combo = practica.cargarCombo(rs, "cmbdept", "");
          }

          %>
          <%=combo%>
          <input type="submit" value="Enviar formulario">
          <%=etiqueta%>
        </form>
    </body>
</HTML>

Interface en Enterprise Java Beans EJB

CREACION Y LLAMADA DE UN JAVABEANS CON INTERFAZ
1.Creamos un nuevo proyecto de JavaAplication y vamos a agregar una Interfaz que será el contrato.
2.Boton derecho sobre el paquete y seleccionamos JavaInterface y llamaremos a nuestra Interfaz: InterfazEJB
3.Escribimos el siguiente código en la Interfaz.
package javaapplication1;
public interface InterfazEJB {
    public void GetMensaje(String nombre);
}
4.A continuación, nos crearemos una clase sobre el mismo paquete que llamaremos ClaseEJB.
  Dicha clase se encargará de implementar la Interfaz de contrato EJB.
5.Debemos utilizar el espacio de nombres siguiente: import javax.ejb.Stateless
6.Al no reconocer la librería de los EJB, tendremos que incluirla manualmente para poder trabajar con dichas
  clases.
7.Boton derecho sobre la carpeta Libraries y pulsamos "Agregar archivo JAR/carpeta". Buscamos la librería de
  ejb que hayamos descargado desde la pagina oficial de Oracle
8.Implementamos la Interfaz sobre la ClaseEJB. Para ello escribimos el siguiente código:
  public class ClaseEJB implements InterfazEJB {
9.Eso nos dará un fallo debido a que tenemos que implementar todos los métodos que contenga la Interfaz
  Debemos pulsar sobre el símbolo de la bombilla de la izquierda del código y nos aparecerá una opción para
  Implementar los métodos abstractos.
10.Una vez que tenemos todos los métodos implementados, escribimos el siguiente código dentro de la clase
  llamada ClaseEJB. Donde el @Override significa sobreescribir el metodo anteriormente definido.
package javaapplication1;
import javax.ejb.Stateless;
public class ClaseEJB implements InterfazEJB
{
    @Override
    public void GetMensaje(String nombre) {
        System.out.println("Primer EJB, Bienvenido a la tecnología " + nombre);
    }
}
11.Creamos un cliente que consuma nuestro bean. Aagregamos una nueva clase sobre el paquete creado y
  la llamaremos ClienteEJB.
package javaapplication1;
import javax.ejb.EJB;
public class ClienteEJB {
@EJB
private ClaseEJB BeanEJB;
    void MetodoCliente()
    {
        BeanEJB = new ClaseEJB();
        BeanEJB.GetMensaje("Usuario de EJB");
    }
}
12.Ahora iremos a la clase inicial de los proyectos de J2SE llamada Main.java. Implementamos el siguiente código:
package javaapplication1;
public class Main {
    public static void main(String[] args) {
        ClienteEJB cliente = new ClienteEJB();

Persistence en Java

Un POJO (Plain Old Java Object) lleva como definición una forma de enfatizar el uso de clases simples que no dependen de ningún framework en especial.
Se conoce como Persistencia a la acción de guardar en un archivo la información de un objeto para luego volver a recuperarla, sin hacer uso de la Ram.
Existen Frameworks que permiten utilizar de forma mas fácil la Persistencia como por ejemplo pueden ser Hibernate, Toplink, etc.
Para poder llevar a cabo la acción de Persistencia podemos utilizar la interfaz Serializable, que serializará un objeto para luego reconstruirlo de nuevo. 
Los campos del objeto marcados con transient no serán serializados.

Ejemplo:

public class serialization {
    public static void main(String args[]){
        try {//serializacion
            //crea el objecto e imprime sus valores por consola
            //clase pojo abajo
            MyPojo object1 = new MyPojo("Hello Persisted Object!", -33, 2.72);
            System.out.println("object1: " + object1);
            //crea un fichero para persistir el objeto
            FileOutputStream fos = new FileOutputStream("persisted-object.file");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            //escribe el objeto serializado a un archivo
            oos.writeObject(object1);
            oos.flush();
            oos.close();
        }catch (Exception e) {
            System.out.println("Exception during serialization: " + e);
            System.exit(0);
        }
        // Deserialización de objeto
        try {
            MyPojo object2;
            //abre el archivo
            FileInputStream fis = new FileInputStream("persisted-object.file");
            ObjectInputStream ois = new ObjectInputStream(fis);
            //lee el objeto del archivo
            object2 = (MyPojo) ois.readObject();
            ois.close();
            //imprime los valores del objeto persistido
            System.out.println("object2: " + object2);
            System.out.println("object2 mystring: " +object2.getMyString());
            //saca por pantalla null porque myTransString está marcado como transient
            System.out.println("object2 mystring2: " +object2.getMyTransString());           
        } catch (Exception e) {
            System.out.println("Exception during deserialization: " + e);
            System.exit(0);
        }
    }   
}
//Pojo serializado
class MyPojo implements Serializable {   
    public MyPojo(String mystring, int intVal1, double doubleVal) {
        this.myString = mystring;
        this.intVal1 = intVal1;
        this.doubleVal = doubleVal;
        this.myTransString = "Este valor no se persistirá.";
    }
    //safety ID
    private static final long serialVersionUID = 1234233342333L;
    //campos para persistir
    private String myString;
    private int intVal1;
    private double doubleVal;   
    //campos que no serán persistidos
    private transient String myTransString;
    //getters y setters
    public String getMyString() {
        return myString;
    }
    public void setMyString(String myString) {
        this.myString = myString;
    }
    public String getMyTransString() {
        return myTransString;
    }
    public void setMyTransString(String myTransString) {
        this.myTransString = myTransString;
    }
    public int getIntVal1() {
        return intVal1;
    }
    public void setIntVal1(int intVal1) {
        this.intVal1 = intVal1;
    }
    public double getDoubleVal() {
        return doubleVal;
    }
    public void setDoubleVal(double doubleVal) {
        this.doubleVal = doubleVal;
    }
    //para sacar todos los valores persistibles por pantalla
    @Override
    public String toString() {
        return "mystring=" + myString + "; intVal1=" + intVal1 + "; doubleVal=" + doubleVal;
    }
}


Persistencia API de Java


Beans de Entidad

Los EJB de entidad están directamente relacionados con los datos de la aplicación, son objetos que mantienen en memoria los datos que maneja la aplicación, las entidades que disponen de persistencia, Noticias, Usuarios, Clientes, etc...

Anotaciones de entidades POJO's:

Anotación @Entity Indica que el objeto es una entidad.
Anotación Table indica la tabla de base de datos que contiene objetos de este tipo. 
Toda entidad debe tener una clave primaria que se debe identificar con la anotación @Id. Además, todas las entidades deben contener un constructor vacío.

Anotaciones @Id, @IdClass, @EmbeddedId
Se emplean para declarar claves primarias de clases, bien de forma simple @Id o compuesta @IdClass y @EmbeddedId.
 
Veamos un código utilizando @IdClass:
 
@Entity 
@IdClass(PersonaId.class) 
public class Persona { 
    @Id 
    private int id; 
    @Id 
    private String nombre;  
    ... 
    public Persona() { } 
    //METODOS Getters y Setters  
} 
 
public class PersonaId { 
     int id; 
    String nombre; 
     public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    } 
}
Y ahora un código utilizando @EmbeddedId
 
@Entity 
public class Persona { 
  @EmbeddedId 
  @AttributeOverrides({ 
     @AttributeOverride(name = "id", column = @Column(name = "ID",  
                              nullable = false, precision = 5, scale = 0)), 
    @AttributeOverride(name = "nombre", column = @Column(name = "NOMBRE"
                       nullable = false, length = 50)), 
 }) 
    private PersonaId id; 
    public Persona() { } 
    //Métodos Getters y Setters  
} 

@Embedded 
public class PersonaId { 
     int id; 
    String nombre; 
     public PersonaId() { } 
     public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    } 
}
@Entity 
@Table(name="PERSONAS") 
public class Persona { 
    @Id 
    private String nombre;  
    @Embedded 
    @AttributeOverrides({@AttributeOverride(name="codigoPostal",  
                                            column=@Column(name="CODIGOPOSTAL")), 
                         @AttributeOverride(name="direccionPostal",  
                                            column=@Column(name="DIRECCIONPOSTAL")) 
    }) 
    private Direccion direccion; 
    public Persona() { } 
    //Métodos Getters y Setters  
} 

@Embeddable  
public class Direccion implements Serializable {  
    private String direccionPostal; 
    private String ciudad; 
    private int codigoPostal; 
    private String pais; 
    public Direccion() { } 
    public boolean equals(Object o) { 
        // Codigo que comprueba si las dos entidades son iguales 
    }   
}

Bean de Sesion

Un bean de sesión representa a un cliente dentro de un servidor J2EE. Para acceder a una aplicación desplegada en el servidor, el cliente invoca métodos del bean de sesión.
Existen dos tipos de Bean de Session:
Stateful (con sesión)
El estado de un objeto consiste en el valor de sus variables de instancia:
  • Mantienen estado de sesión.
  • Cada cliente usa una instancia del bean.
  • Se especifican mediante la anotación @Stateful.
Stateless (sin sesión)
Una sesión stateless no mantiene un estado conversacional para un cliente en particular. Puede servir a muchos clientes.
  • No mantienen el estado del cliente.
  • El contenedor crea pools de Session bean stateless usados por n clientes.
  • Se especifican mediante la anotación @Stateless
Ciclo de vida Bean @StateFul:
  • Creación: Cuando el cliente ejecuta el método create().
  • Uso: Cuando el cliente llama un método de negocio.
  • Desactivación: El bean se manda a memoria secundaria.
  • Activación: El bean es despertado para servir algún método al cliente.
  • Destrucción: Cuando el cliente termina su sesión con el bean.
Ciclo de vida Bean @Stateless:
  • Creación: Cuando el contenedor quiere un bean.
  • Uso: Cuando el cliente llama un método de negocio.
  • Destrucción: Cuando el contenedor decide que hay muchos beans en el pool.
Métodos invocados automáticamente por el EJB container:
No tienen parámetros y son tipos de métodos void.

Los métodos anotados con @PostConstruct son invocados cuando se instancia un bean, después de la inyección de las anotaciones y antes de que se invoque el primer método de negocio.
Los métodos anotados con @PreDestroy son invocados después de la ejecución de métodos anotados con @Remove y antes de que el contenedor elimine la instancia del bean.
Los métodos anotados con @PostActivate son invocados después de que el contenedor mueva el bean de memoria secundaria a estado activo.
Los métodos anotados con @PrePassivate son invocados antes de que el contenedor elimine el bean del ambiente activo y lo salve en la memoria secundaria.
Un método anotado con @Remove es un método de ciclo de vida especial, que puede ser invocado por el cliente del bean.
Después de su ejecución el contenedor llama al método anotado con @PreDestroy, si existe.
A continuación la instancia queda sin referencia y lista para ser procesada con el Garbage Collector (recolector de basura).

Acceso de Clientes Bean a una Sesión:
Un cliente sólo puede acceder un Session Bean a través de los métodos definidos en la Interfaz. Es el tipo de cliente quién determina que interfaces se usan. Pueden haber dos tipos de interfaces:
  • Remotas.
  • Locales.
Un cliente remoto de un session bean:
  • Corre en una máquina virtual de Java diferente a la del bean.
  • Puede ser un programa standalone, un componente web (servlet o JSP) u otro EJB.
  • La localización del bean es transparente para el cliente.
  • Para definirlo debe usarse la anotación @Remote y puede hacerse de dos formas diferentes.
Un cliente local de un session bean:
  • Debe correr en la misma máquina virtual de Java que el bean.
  • Normalmente es otro EJB.
  • La localización del bean NO es transparente para el cliente.
  • Para definirlo debe usarse la anotación @Local en la declaración de la interfaz o nada, que es por defecto.
@Local
public interface MiBeanLocal 
{
//CODIGO DEL BEAN
}
  • Debemos especificar la clase principal con la anotación @Local con la interfaz como parámetro:
 
@Local(MiBeanLocal.class)
public class MiBean implements MiBeanLocal 
{
//CODIGO DE LA CLASE
}
 

Factores a considerar para utilizar acceso local o acceso remoto:

  • Beans relacionados entre sí deben usar interfaces locales para comunicarse entre ellos.
  • Clientes en otras máquinas virtuales o reales diferentes deben usar interfaces remotas.