lunes, 19 de diciembre de 2011

Introducción JPA- Java Persistence API


Hace algún tiempo había leído bastante sobre JPA, incluso antes de que naciera JPA tuve que trabajar con EJB 2.0 así que una de las cosas interesantes sobre ORM (Object Relational Mapping) están en JPA.


Con EJB 2.0 resultaba bastante incómodo trabajar con los ficheros xml de configuración, las diferentes interfaces local, remota y el propio bean, con JPA y las anotaciones de JAVA esto desaparece, pero no voy a correr tanto, en este artículo me voy a limitar a hablar de JPA, usando un ejemplo sencillo y usando la implementación que hace hibernate de JPA.


He utilizado la siguiente tecnología:
  • MySQL 5.1.xx
  • MySQL workbench
  • Eclipse Indigo
  • JAVA SE 7
  • JBOSS 5.0.1


Definición:


Antes de nada comentar que es JPA. Según la definición de la wikipedia:


Es la API de persistencia desarrollada para la plataforma Java EE
Es un framework del lenguaje de programación Java que maneja datos relacionales en aplicaciones usando la Plataforma Java en sus ediciones Standard (Java SE) y Enterprise (Java EE).
La JPA fue originada a partir del trabajo del JSR 220 Expert Group. Ha sido incluida en el estándar EJB3.
Persistencia en este contexto cubre tres áreas:
  • La API en sí misma, definida en javax.persistence.package
  • La Java Persistence Query Language (JPQL)
  • Metadatos objeto/relacional
El objetivo que persigue el diseño de esta API es no perder las ventajas de la orientación a objetos al interactuar con una base de datos (siguiendo el patrón de mapeo objeto-relacional), como sí pasaba con EJB2, y permitir usar objetos regulares (conocidos como POJOs).






El ejemplo:


Aunque he empezado primero creando las tablas en un esquema “JPA”, dentro de mysql, podría haber empezado cranto los beans de java y con estos establecer una conexión a MySql y se habrían creado de todas formas.


  • He creado las siguientes tablas con MySQL workbench (conectando a MySQL y generando las tablas):

  • Usando las funciones JPA de eclipse, he creado un proyecto de tipo JPA, y creado los beans entity usando la función “JPA entities from tables”, al final el código generado es el siguiente:


Departament.java:


package jpa.beans;
import java.io.Serializable;
import javax.persistence.*;
import java.util.Set;
/**
* The persistent class for the Departament database table.
*
*/
@Entity
public class Departament implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private int idDepartament;
private String name;
//bi-directional many-to-one association to Person
@OneToMany(mappedBy="departament")
private Set<Person> persons;
public Departament() {
}
public int getIdDepartament() {
return this.idDepartament;
}
public void setIdDepartament(int idDepartament) {
this.idDepartament = idDepartament;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Set<Person> getPersons() {
return this.persons;
}
public void setPersons(Set<Person> persons) {
this.persons = persons;
}
}


Person.java:


package jpa.beans;
import java.io.Serializable;
import javax.persistence.*;
/**
* The persistent class for the Person database table.
*
*/
@Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private PersonPK id;
private String address;
private String country;
@Column(name="`first name`")
private String first_name;
@Column(name="`last name`")
private String last_name;
@Column(name="`zip code`")
private String zip_code;
//bi-directional many-to-one association to Departament
@ManyToOne
@JoinColumn(name="Departament_idDepartament",insertable=false,updatable=false)
private Departament departament;
public Person() {
}
public PersonPK getId() {
return this.id;
}
public void setId(PersonPK id) {
this.id = id;
}
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
public String getFirst_name() {
return this.first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return this.last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getZip_code() {
return this.zip_code;
}
public void setZip_code(String zip_code) {
this.zip_code = zip_code;
}
public Departament getDepartament() {
return this.departament;
}
public void setDepartament(Departament departament) {
this.departament = departament;
}
}
PersonPK.java:
package jpa.beans;
import java.io.Serializable;
import javax.persistence.*;
/**
* The primary key class for the Person database table.
*
*/
@Embeddable
public class PersonPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
private int idPerson;
@Column(name="Departament_idDepartament")
private int departament_idDepartament;
public PersonPK() {
}
public int getIdPerson() {
return this.idPerson;
}
public void setIdPerson(int idPerson) {
this.idPerson = idPerson;
}
public int getDepartament_idDepartament() {
return this.departament_idDepartament;
}
public void setDepartament_idDepartament(int departament_idDepartament) {
this.departament_idDepartament = departament_idDepartament;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PersonPK)) {
return false;
}
PersonPK castOther = (PersonPK)other;
return
(this.idPerson == castOther.idPerson)
&& (this.departament_idDepartament == castOther.departament_idDepartament);
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + this.idPerson;
hash = hash * prime + this.departament_idDepartament;
return hash;
}
}
  • Hice un pequeño Main de pruebas:


package jpa.main;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import jpa.beans.Departament;
public class Main {
public static void main(String args[])throws Exception{
EntityManagerFactory emf=Persistence.createEntityManagerFactory("JPATestJv");
EntityManager em=emf.createEntityManager();
try{
EntityTransaction entr=em.getTransaction();
entr.begin();
Departament dep = new Departament();
dep.setIdDepartament(0);
dep.setName("Marketing");
dep.setPersons(null);
em.persist(dep);
entr.commit();
}finally{
em.close();
}
}
}
  • El fichero persistence.xml:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="JPATestJv" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>jpa.beans.Departament</class>
<class>jpa.beans.Person</class>
<class>jpa.beans.PersonPK</class>
<properties>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/JPA"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.password" value="JPA"/>
<property name="hibernate.connection.username" value="JPA"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>




  • El resultado de la ejecución del Main:


Hibernate: insert into Departament (name, idDepartament) values (?, ?)


  • Después se realiza la select para ver si se han insertado los datos:


mysql> select * from Departament;
+---------------+-----------+
| idDepartament | name |
+---------------+-----------+
| 0 | Marketing |
+---------------+-----------+
1 row in set (0.00 sec)

domingo, 11 de diciembre de 2011

La combinación de bibliotecas compartidas y extendedDocumentRoot en WebSphere 6.1




WebSphere 6.1 es un servidor de aplicaciones java quizá un poco sobre cargado en funciones y en diseño, pero entre esta funciones existen algunas que me ha resultado interesantes en el momento que tenia la necesidad de compartir páginas JSP y añadir al classloader del módulo WAR algunos JARS. La combinación de estas dos tecnologías permite crear funcionalidades fuera del contexto web y accesibles desde éste, en definitiva permite compartir recursos entre contextos.

Por una lado se debe utilizar la propiedad de motos de JSP “extendedDocumentRoot”, que tal cómo se indica en la ayuda infocenter de WebSphere v6.1:

extendedDocumentRoot:
Para compartir un recurso de archivo JSP en todos los archivos de aplicación web, especifique una lista de valores separados por comas de directorios o archivos de archivado Java (JAR) o ambos, como vías de acceso de búsqueda a utilizar si el recurso solicitado no se encuentra en el árbol público de documentos del archivo de la aplicación web. Si la petición es una petición parcial válida para un archivo de bienvenida, se devuelve un error 404. Si el archivo JSP se encuentra dentro de un archivo JAR y reloadEnabled es true, se utilizará la indicación de la hora del archivo JAR para las comprobaciones de isOutDated a efectos de recompilación. El valor por omisión de este parámetro es nulo.”

En pocas palabras, dentro del fichero “ibm-web-ext.xmi”, se añade la propiedad de motor de JSP “extendedDocumentRoot” que apunte a un directorio contenedor de JSPs, y el resultado es que estás JSPs són accesibles desde el contexto web.

Ejemplo de fichero “extendedDocumentRoot”:



<?xml version="1.0" encoding="UTF-8"?>
<webappext:WebAppExtension xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:webappext="webappext.xmi" xmi:id="WebAppExtension_1323629617174" reloadInterval="3" reloadingEnabled="true" additionalClassPath="" fileServingEnabled="true" directoryBrowsingEnabled="false">
<webApp href="WEB-INF/web.xml#WebApp_ID"/>
<jspAttributes xmi:id="JSPAttribute_1"
name="extendedDocumentRoot" value="/opt/IBM/WebSphere/jsps"/>

<jspAttributes xmi:id="JSPAttribute_2"
name="trackDependencies" value="true"/>
</webappext:WebAppExtension>


Nota: Además de la propiedad extendedDocumentRoot, se ha utlizado la propiedad trackDependencies, en este caso útil si se quieren realizar includes estáticos de JSP y se pretende forzar la compilación siempre.



Bien, una vez comentado cómo compartir páginas es interesante comentar cómo añadir conjuntos de JARs, bibliotecas compartidas, al classloader de módulo (WAR).
 Para ello se puede utilizar la funcionalidad de biblioteca compartida que implementa WebSphere. Hay que comentar que una biblioteca compartida puede estar a nivel de classloader de servidor, de aplicación EAR o de módulo web (WAR), en este caso resulto muy útil utilizarlo a nivel de WAR.

Lo primero y antes de seguir es importante definir que es una biblioteca compartida:


Las bibliotecas compartidas son archivos utilizados por varias aplicaciones. Toda biblioteca compartida consta de un nombre simbólico, una classpath de Java y una vía de acceso original para cargar las bibliotecas JNI (Java Native Interface). Puede utilizar las bibliotecas compartidas para reducir el número de archivos de biblioteca duplicados en el sistema. “

En definitiva, una biblioteca compartida no es nada más que un nombre lógico asociado a un classpath que contiene uno o varios JARs y estos son accesibles desde las diferentes aplicaciones, contextos o servidores que en su definición dicen utilizar esta biblioteca.

Para definir una biblioteca compartida se puede utilizar la consola administrativa de WAS:

Entorno > Bibliotecas compartidas > nombre_biblioteca_compartida.

Y para determinar por ejemplo que un módulo web utiliza la biblioteca:

  • Pulse Aplicaciones > Aplicaciones de empresa > nombre_aplicación >Referencias de bibliotecas compartidas en el árbol de navegación de la consola para acceder a la página de bibliotecas compartidas.
  • En la página Referencias de bibliotecas compartidas, seleccione una aplicación o un módulo con los que desee asociar una biblioteca compartida.
(http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp)

Combinando estas dos funcionalidades se puede conseguir las páginas JSP de “extendedDocumentRoot” tengan funcionalidades implementadas dentro de una biblioteca compartida y todo esto accesible desde los contextos que lo quieran utilizar y sin necesidad de estar desplegado en el propio módulo.