openxava
/ documentación /
Guía para la migración mental de OX2 a OX3
Este guía es para ayudar a un desarrollador OpenXava 2 a empezar
rápidamente con OpenXava 3.
OX3 será completamente compatible con OX2 y podremos ejecutar nuestras
aplicaciones OX2 usando OX3, además podemos continuar desarrollando con
XML en OX3.
Definición
de componentes
La principal diferencia entre OX2 y OX3 es el formato para definir
componentes.
En OX2 usamos XML, como sigue:
<componente nombre="Profesor">
<entidad>
<propiedad nombre="codigo" tipo="String"
clave="true" longitud="5" requerido="true"/>
<propiedad nombre="nombre" tipo="String"
longitud="40" requerido="true"/>
<coleccion nombre="alumnos">
<referencia modelo="Alumno"/>
</coleccion>
<metodo nombre="anadirAlumno">
<calculador clase="org.openxava.escuela.calculadores.CalculadorAnadirAlumno"/>
</metodo>
</entidad>
<mapeo-entidad tabla="MYSCHOOL@separator@TEACHER">
<mapeo-propiedad
propiedad-modelo="codigo" columna-tabla="ID"/>
<mapeo-propiedad
propiedad-modelo="nombre" columna-tabla="NAME"/>
</mapeo-entidad>
</componente>
En OX3 usamos Java con anotaciones:
package org.openxava.escuela.modelo;
import javax.persistence.*;
import org.openxava.annotations.*;
/**
*
* @author Javier Paniza
*/
@Entity
public class Profesor {
@Id @Column(length=5) @Required
private String codigo;
@Column(length=40) @Required
private String nombre;
public String getCodigo() {
return codigo;
}
public void setCodigo(String codigo) {
this.codigo = codigo;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
Ahora podemos escribir la lógica directamente en nuestras clases Java, por
tanto no es necesario usar calculadores para propiedades calculadas o
métodos.
El mapeo objeto-relacional se hace mediante las anotaciones JPA. Y el
resto (tab y vista) es una traducción literal del XML de OpenXava a
anotaciones Java.
Agregados
Los agregados ya no existen en OX3. Pero podemos simular los agregados
usando objetos embebidos para las referencias simples, y colecciones de
entidades con borrar en cascada para las colecciones.
Es decir, en OX escribiamos:
<entidad>
<referencia nombre="direccion" modelo="Direccion"/>
</entidad>
<agregado nombre="Direccion">
...
</agregado>
Y en OX3 escribimos:
// Cliente.java
@Entity
public class Cliente {
@Embedded
private Direccion direccion;
}
// Direccion.java
@Embeddable
public class Direccion {
}
Como podemos ver, usamos objetos embebidos JPA que funcionan exactamente
igual que los agregados de OpenXava para el caso de una referencia simple.
Para colecciones de agregados con OX2 escribimos:
<componente nombre="Factura">
<entidad>
<coleccion nombre="lineas">
<referencia modelo="LineaFactura"/>
</coleccion>
</entidad>
<agregado nombre="LineaFactura">
...
</aggregate>
</component>
Pero en OX3 escribimos:
// Factura.java
@Entity
public class Factura {
@OneToMany (mappedBy="factura", cascade=CascadeType.REMOVE)
private Collection<LineaFactura> details;
}
// LineaFactura.java
@Entity
public class LineaFactura {
}
Es decir, usamos una coleccion de entidades con borrar en cascada, y esto
se comporta como una colección de agregados de OX2.
Valores
posibles
Los
<valores-posibles/> de OX2 se implementan en OX3
usando
enums de Java 5.
Es decir, en OX2 escribimos:
<propiedad nombre="distancia">
<valores-posibles>
<valor-posible valor="local"/>
<valor-posible valor="nacional"/>
<valor-posible valor="internacional"/>
</valores-posibles>
</propiedad>
Su equivalente en OX3 es:
private Distancia distancia;
public enum Distancia { LOCAL, NACIONAL, INTERNACIONAL };
Tiene el mismo efecto, con la diferencia de que:
- El tipo de dato no es int, sino Distancia.
- Al grabar en la base de datos los valores posibles graban por
defecto 0 para vacío, 1 para local, 2 para naciona y 3 para
internacional, mientras que el enum graba nulo para vacío, 0
para LOCAL, 1 para NACIONAL y 2 para INTERNACIONAL.
Es decir, si queremos usar una database de OX2 con OX3 necesitamos usar un
Hibernate Type, como sigue:
@org.hibernate.annotations.Type(type="org.openxava.types.Base1EnumType",
parameters={
@Parameter(name="enumType", value="org.openxava.test.modelo.LineaFactura$TipoServicio")
}
)
private TipoServicio tipoServicio;
public enum TipoServicio { ESPECIAL, URGENTE }
Base1EnumType es un Hibernate Type incluido en OpenXava para
grabar un
enum de Java 5 usando índice de base 1. De esta manera
podemos ir contra la base de datos de nuestra aplicación OX3 usando OX2.
Calculadores
valor defecto al crear
El
calculador-valor-defecto de OX2 esta disponible en OX3
mediante la anotación
@DefaultValueCalculator. Pero
@DefaultValueCalculator
no soporta
al-crear="true" de su homólogo en XML. Hemos de
simular el efecto de este tipo de calculador usando su equivalente en JPA.
Para un id autogenerado podemos usar la anotacion JPA
@GeneratedValue.
Por ejemplo, en OX2 escribimos:
<propidad nombre="codigo" tipo="Integer" clave="true" longitud="5" requerido="false">
<calculador-valor-defecto clase="org.openxava.calculators.IdentityCalculator" al-crear="true"/>
</propiedad>
En OX3 escribimos:
@Id @Column(length=5)
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer codigo;
Es decir, código JPA puro y duro.
Pero
<calculador-valor-defecto ... al-crear="true"/> es
más flexible que
@GeneratedValue porque admite cualquier lógica,
y puede ser usada por propiedades no clave. En este caso podemos usar un
método
@PrePersist de JPA. Veamos un ejemplo. Cuando en OX2
escribimos:
<propiedad nombre="oid" tipo="String" clave="true" oculta="true">
<calculador-valor-defecto
clase="org.openxava.test.calculadores.CalculadorLineaFacturaOid"
al-crear="true"/>
</propiedad>
Donde
InvoiceDetailOidCalculator tiene una lógica personalizada
para generar el valor. Esto puede ser fácilmente traducido a OX3 añadiendo
el siguiente método a nuestro POJO:
@PrePersist
private void calcularOID() {
oid = factura.getAño() + ":" + factura.getNumero() + ":" + System.currentTimeMillis();
}
En este caso la lógica del método
calcularOID is la misma que la
de
InvoiceDetailOIDCalculator. Y una vez más usamos código JPA
puro y duro.
Calculadores
de retrollamada: poscrear, postmodificar, poscargar, preborrar and
posborrar
En lugar de
<calculador-poscrear/>,
<calculador-posmodificar/>,
<postload-calculator/>,
<preremove-calculator/>
y
<postremove-calculator/> en OX3 usamos los métodos de
retrollamada de JPA, es decir, métodos en nuestras entidades anotados con
@PrePersist,
@PostPersist,
@PreRemove,
@PostRemove,
@PreUpdate,
@PostUpdate y
@PostLoad.
Por ejemplo, si tenemos una colección como esta en un componente
Factura:
<coleccion nombre="lineas">
<referencia modelo="LineaFactura"/>
<calculador-posborrar
clase="org.openxava.test.calculadores.CalculadorPosborrarLineaFactura"/>
</coleccion>
Con esta clase calculador:
package org.openxava.test.calculadores;
import java.rmi.*;
import org.openxava.calculators.*;
import org.openxava.test.ejb.*;
/**
* @author Javier Paniza
*/
public class CalculadorPosborrarLineaFactura implements IModelCalculator {
private IFactura factura;
public Object calculate() throws Exception {
factura.setComentario(factura.getComentario() + "DETALLE BORRADO");
return null;
}
public void setModel(Object modelo) throws RemoteException {
this.factura = (IFactura) modelo;
}
}
En OX3 solo necesitamos tener un método como este en la clase
LineaFactura:
@PostRemove
private void despuesDeBorrar() {
factura.setComentario(factura.getComentario() + "DETALLE BORRADO");
}
En este caso, más fácil que en OX2.
Conversores
Los utilísimos conversores de OpenXava no están disponibles en OX3.Pero no
nos debemos preocupar demasiado, porque podemos usar Type de Hibernate,
que proporciona casi toda la funcionalidad de los conversores de OpenXava.
Mira la
Referencia de Hibernate sobre Type,
Type de Hibernate tiene algunos inconvenientes sobre los clásicos
conversores de OpenXava:
- Los conversores por defecto (del archivo defaults-converter.xml y
conversores.xml) no están soportados.
- Conversores para referencias no se soportan.
persistence.xml
en vez de configuration
En el build.xml la propiedad 'configuration' ya no se usa. Ahora en vez de
los archivos de propiedades podemos comentar y descomentar código en
persistence.xml para poder trabajar con varias configuraciones, de esta
manera:
<!-- Tomcat + Hypersonic -->
<persistence-unit name="default">
<non-jta-data-source>java:comp/env/jdbc/MySchoolDS</non-jta-data-source>
<class>org.openxava.session.GalleryImage</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
</properties>
</persistence-unit>
<!-- JBoss + Hypersonic
<persistence-unit name="default">
<non-jta-data-source>java:/MiEscuelaDS</non-jta-data-source>
<class>org.openxava.session.GalleryImage</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
</properties>
</persistence-unit>
-->
<!-- WebSphere + AS/400
<persistence-unit name="default">
<non-jta-data-source>jdbc/MiEscuelaDS</non-jta-data-source>
<class>org.openxava.session.GalleryImage</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.DB2400Dialect"/>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
-->
Aquí tenemos 3 configuraciones y podemos activar una de ellas fácilmente
con solo comentar o descomentar.
Pruebas
JUnit: Poniendo valor a un combo para una referencia con clave compuesta
En OX3 ésta es la forma de poner valor a un combo con clave compuesta:
Envio envio = (Envio) Envio.findAll().iterator().next();
setValue("envio.KEY", toKeyString(envio));
Como se ve, hemos de usar el método toKeyString() (incluido en
ModuleTestBase), y no el método toString() del POJO.
Esta técnica también funciona con OX2.
Propiedad
de vista
Las propiedades de vista no existen en OX3. Pero es fácil simularlos
usando
propiedades
transitorias en nuestro modelo.
Es decir, en OX2 escribimos:
<vista>
<propiedad nombre="entregadoPor">
<valores-posibles>
<valor-posible valor="empleado"/>
<valor-posible valor="transportista"/>
</valores-posibles>
<calculador-valor-defecto
clase="org.openxava.calculators.IntegerCalculator">
<poner propiedad="value" valor="0"/>
</calculador-valor-defecto>
</propiedad>
<vista-propiedad propiedad="entregadoPor">
<al-cambiar clase="org.openxava.test.actiones.AlCambiarEntregadoPor"/>
</vista-propiedad>
...
</vista>
Y ahora con OX3 escribimos:
@Transient
@DefaultValueCalculator(value=EnumCalculator.class,
properties={
@PropertyValue(name="enumType", value="org.openxava.test.modelo.Albaran$EntregadoPor")
@PropertyValue(name="value", value="TRANSPORTISTA")
}
)
@OnChange(AlCambiarEntradoPor.class)
private EntragadoPor entregadoPor;
public enum EntregadoPor { TRABAJADOR, TRANSPORTISTA }
Con el mismo efecto.