Si quieres migrar a 7.0 o superior mira las instrucciones de migración nuevas
assertValue("date", "5/25/21");
assertValue("date", "5/25/2021");
setConditionValues("1");
execute("List.filter"); // Esto no graba "Código = 1" automáticamente
// Aquí haces más cosas
selectListConfiguration("Código = 1"); // FALLA EN v6.5
setConditionValues("1");
execute("List.filter");
execute("List.saveConfiguration"); // AÑADE ESTAS LÍNEAS PARA GRABAR...
execute("SaveListConfiguration.save"); // ...LA CONSULTA EXPLÍCITAMENTE
// Aquí haces más cosas
selectListConfiguration("Código = 1"); // Ahora funciona
String [] acciones = {
"Print.generatePdf",
"Print.generateExcel",
"ImportData.importData",
"CRUD.new",
"CRUD.deleteSelected",
"CRUD.deleteRow",
"List.filter",
"List.orderBy",
"List.viewDetail",
"List.hideRows",
"List.sumColumn",
// "List.changeConfiguration", // QUITA ESTA LÍNEA
"List.changeColumnName",
"ListFormat.select"
};
assertActions(acciones);
assertLabelInList(0, "Year of Invoice");
assertLabelInList(0, "Year of invoice");
execute("List.goNextPage", "collection=miColeccion");
execute("Collection.edit", "row=10,viewObject=xava_view_miColeccion");
setValue("nombre", "Nombre modificado");
execute("Collection.save");
// execute("List.goNextPage", "collection=miColeccion"); // QUITA ESTA LÍNEA
assertValueInCollection("miColeccion", 0, 0, "Nombre modificado");
<% if (org.openxava.web.Browsers.isIE(request)) { %>
<script type='text/javascript' src="<%=request.getContextPath()%>/xava/js/css-vars-ponyfill.js?ox=<%=oxVersion%>"></script>
<script type='text/javascript'>cssVars({ }); </script>
<% } %>
String applicationName = request.getContextPath().substring(1);
MetaApplication metaApplication = MetaApplications.getMetaApplication(applicationName);
MetaApplication metaApplication = MetaApplications.getMainMetaApplication();
execute("ImageEditor.changeImage", "newImageProperty=imagen");
String urlImagen = System.getProperty("user.dir") + "/imagenes-pruebas/pastel.gif";
setFileValue("newImage", urlImagen);
execute("LoadImage.loadImage");
uploadFile("imagen", "imagenes-pruebas/pastel.gif");
execute("Gallery.edit", "galleryProperty=fotos");
execute("Gallery.addImage");
String urlImagen = System.getProperty("user.dir") + "/imagenes-pruebas/foto-javi.png";
setFileValue("newImage", urlImagen);
execute("LoadImageIntoGallery.loadImage");
execute("Gallery.close");
uploadFile("fotos", "imagenes-pruebas/foto-javi.png");
@View(members="numero, descripcion, pantallazos, fecha")
@View(members="numero, descripcion; pantallazos; fecha")
@View(members="numero, descripcion, adjuntos, fecha")
@View(members="numero, descripcion; adjuntos; fecha")
@View(members=
"datos [ #titulo;" +
" fechaEstreno, director;" +
" guionistas, elenco;" +
" guiones;" + // ARCHIVOS
"]"
)
@View(members=
"datos [ #titulo;" +
" fechaEstreno, director;" +
" guionistas, elenco;" +
"]" +
"guiones [ guiones ]"
)
HtmlPage page = (HtmlPage) getWebClient().getCurrentWindow().getEnclosedPage(); waitAJAX(); // AÑADE ESTA LÍNEA ANTES DE USAR page
Si tus pruebas usan métodos de ModuleTestBase exclusivamente no has de hacer ningún cambio.
Bean Validation 2.0 es compatible con 1.1, por lo que la mayoría de tu código seguirá funcionando como siempre. Sin embargo, Bean Validation 2.0 incluye algunas anotaciones nuevas que antes estaban en Hibernate Validator, por eso si importabas los dos, Hibernate Validator y Bean Validation, puede que tu código no compile. Por ejemplo:
import javax.validation.constraints.*; import org.hibernate.validator.constraints.*; public class MiClase { // @NotBlank // AHORA COMPILA CON ERROR
@javax.validation.constraints.NotBlank // CALIFICA LA ANOTACIÓN
private String callePrincipal;
Ahora @NotBlank está tanto en javax.validation.constraints como en org.hibernate.validator.constraints. Simplemente quita el import de org.hibernate.validator.constraints o, si todavía necesitas Hibernate Validator, califica la anotación @NotBlank (es decir usa @javax.validation.constraints.NotBlank).
Si usas directamente el API de HtmlUnit, vía getWebClient() o getHtmlPage() de ModuleTestBase, tienes que adaptar tu código al nuevo HtmlUnit. Hemos recolectado aquí algunos cambios que hemos notado.
Por ejemplo, los métodos que empiezan por getHtmlElement... ahora son getElement..., sólo has de quitar el Html. Por tanto, deberías cambiar:
HtmlElement i = chartsLink.getHtmlElementsByTagName("i").get(0);
Por:
HtmlElement i = chartsLink.getElementsByTagName("i").get(0);
El método click() ya no lanza el evento onclick. Por lo que has de cambiar:
boton.click();
Por:
getHtmlPage().executeJavaScript(boton.getOnClickAttribute());
Cambiar el valor de un HtmlInput ya no lanza el evento onchange, por lo que has de cambiar:
input.setValueAttribute(valor);Por:
input.setValueAttribute(valor); if (!Is.emptyString(input.getOnChangeAttribute())) { input.fireEvent(Event.TYPE_CHANGE); }
Y posiblemente más cosas, consulta la documentación de HtmlUnit.
Por causa del nuevo HtmlUnit ya no tenemos manera de cambiar el lenguaje sin reiniciar el módulo. Por tanto, puede que necesites adaptar algunas de tus pruebas. Por ejemplo:
execute("CRUD.new"); ... setLocale("es"); // Aquí el módulo se reinicia execute("CRUD.new"); // AÑADE ESTA LÍNEA ...
En este caso el setLocale() reinicia el módulo empezando en modo lista, por eso llamamos a CRUD.new, para que la prueba continue funcionando como siempre.
Dado que usamos el API de JPA en nuestras aplicaciones la mayoría de nuestro código debería funciona sin ningún cambio. Sin embargo, puede que encuentres algún detalle que funcione ligeramente diferente. Por ejemplo, para esta definición de entidad:
public class Cliente { @OneToMany private Collection<Vendedor> vendedores;
El siguiente código ha dejado de funcionar:
cliente.setVendedores(Collections.emptySet());Ahora has de escribir:
cliente.setVendedores(new ArrayList<>());
Quizás encuentres alguna que otra cosa que tengas que ajustar, consulta la documentación de Hibernate. De todos modos, en los puntos que siguen explicamos los cambios más importantes relacionados con Hibernate 5.3.
Hibernate 5.3 tiene un bug que duplica las claves foráneas si usas hibernate.hbm2ddl.auto=update contra la base de datos que estabas usando con Hibernate 4.3. Si tu base de datos es tolerante con las claves duplicadas no hay problema, en caso contrario puede que tengas algunas advertencias e incluso errores. De momento, la única solución a esto es quitar todas las claves foráneas de tu base de dato manualmente, de tal forma que Hibernate las cree correctamente al arrancar.
En Hibernate 5.3 la siguiente configuración para persistence.xml no funciona:
<properties> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties>
Has de definir un hibernate.default_schema para que vaya. Si no usas un esquema simplemente define el esquema por defecto para tu base de datos, por ejemplo PUBLIC para HSQLDB, así:
<properties> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.default_schema" value="PUBLIC"/> </properties>Por otra parte, la tarea ant actualizarEsquema funciona perfectamente incluso sin definir esquema.
Con Hibernate 5.3 hibernate.default_schema ya no tiene efecto en MySQL, ahora has de usar hibernate.default_catalog en su lugar. A partir de OpenXava 6.1.2 hemos modificado la API de OpenXava para que funcione como siempre, reconociendo hibernate.default_schema incluso para MySQL. Sin embargo, la generación automática de esquema al iniciar la aplicación (es decir hibernate.hbm2dll.auto=update o javax.persistence.schema-generation.database.action=update) es asunto de Hibernate, por lo que no podemos arreglarlo. Si usas esta funcionalidad, deberías cambiar hibernate.default_schema por hibernate.default_catalog.
Resumiendo, si usas MySQL cambia en persistence.xml:
<properties> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.default_schema" value="EMPRESAB"/> </properties>
Por:
<properties> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.default_catalog" value="EMPRESAB"/> </properties>
Fíjate, hibernate.default_catalog en vez de hibernate.default_schema. Sólo para MySQL.
En Hibernate 5.3 @GeneratedValue(strategy=GenerationType.AUTO) ya no es IDENTITY por defecto, al menos para HSQLDB. Por tanto, si usas HSQLDB y no quieres cambiar tu estructura de tablas has de cambiar:
@Id
@GeneratedValue(strategy=GenerationType.AUTO) // O @GeneratedValue a secas private int id;
Por esto:
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) private int id;
En tus propiedades id con GenerationType.AUTO.
En Hibernate 5.3 han cambiado la definición de los métodos para los conversores de tipo, los que se usan en la anotación @Type. Si has creado tu propio conversor de tipo Hibernate has de hacer algunas ligeras modificaciones en la definición de tus métodos, concretamente has de cambiar SessionImplementer por SharedSessionContractImplementor como parámetro. Por ejemplo, deberías cambiar esta línea:
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor sessionImplementor, Object owner) throws HibernateException, SQLException {
Por esta otra:
public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor sessionImplementor, Object owner) throws HibernateException, SQLException {
Lo mismo para todos los métodos que usen SessionImplementer en cualquier implementación de UserType o CompositeUser.
Para que el nuevo Hibernate 5.3 funcione con Liferay has de crear un archivo portal-ext.properties en la carpeta webapps/ROOT/WEB-INF/classes del Tomcat en tu Liferay, con la siguiente entrada:
portal.security.manager.strategy=none
Si no has modificado tus página de bienvenida y primeros pasos has de borrar web/naviox/welcome.jsp y web/naviox/firstSteps.jsp de tu proyecto antes de hacer el actualizarOX. Si tienes tus propias páginas de bienvenida o primeros pasos las tienes que editar para cambiar las referencias a naviox.css (como se explica abajo) y quitar todas las referencias a la clase Java NaviOXStyle que ya no existe.
Además, se ha movido de Addons a OpenXava. Por tanto, si lo usas en algún JSP personalizado, como welcome.jsp, has de cambiar:
<link href="<%=request.getContextPath()%>/naviox/style/naviox.css?ox=<%=oxVersion%>"
rel="stylesheet" type="text/css">
<link href="<%=request.getContextPath()%>/xava/style/light.css?ox=<%=oxVersion%>" rel="stylesheet" type="text/css">
Mejor todavía, puedes incluir el CSS del tema actual en lugar de un CSS ad hoc, de esta manera:
<link href="<%=request.getContextPath()%>/xava/style/<%=XavaPreferences.getInstance().getStyleCSS()%>?ox=<%=oxVersion%>" rel="stylesheet" type="text/css">
getView().findObject();
if (!getView().findObject()) { addError("object_with_key_not_found") }
execute("Mode.detailAndFirst"); -- Cambiar por --> execute("List.viewDetail", "row=0");
execute("DetailList.detailAndFirst"); -- Cambiar por --> execute("List.viewDetail", "row=0");
execute("DetailList.list"); -- Cambiar por --> execute("Mode.list");
execute("Mode.split"); // Quita esta línea de tus pruebas, quizás tengas que adaptar tu prueba
private String [] acciones = {
"Print.generatePdf",
"Print.generateExcel",
"ImportData.importData",
"CRUD.new",
"CRUD.deleteSelected",
"CRUD.deleteRow",
// "Mode.detailAndFirst", // QUITA ESTA LÍNEA
// "Mode.split", // QUITA ESTA LÍNEA
"List.filter",
"List.orderBy",
"List.viewDetail",
"List.hideRows",
"List.sumColumn",
"List.changeConfiguration",
"List.changeColumnName",
"ListFormat.select"
}; assertActions(acciones);
execute("CRUD.new"); execute("Collection.new", "viewObject=xava_view_edificios");
execute("CRUD.new"); setValue("nombre", "ALGO"); // AÑADE ESTO execute("Collection.new", "viewObject=xava_view_edificios");
execute("List.viewDetail", "row=0"); // En lugar de: execute("CRUD.new"); execute("Collection.new", "viewObject=xava_view_edificios");
String [] acciones = { "Navigation.previous", "Navigation.first", "Navigation.next", "CRUD.new", "CRUD.save", "CRUD.delete", "CRUD.refresh", "Mode.list", "Mode.split", "Collection.new", "Collection.removeSelected", "CollectionCopyPaste.cut", // AÑADE ESTA ENTRADA "Print.generatePdf", "Print.generateExcel", "List.filter", "List.orderBy", "List.sumColumn", // SIEMPRE PRESENTE, INCLUSO CON LA COLECCIÓN VACÍA "List.changeColumnName" }; assertActions(acciones);
assertValueInList(2, 4, "Yes");Deberías cambiarla por:
assertValueInList(2, 4, "Pagado");Si la etiqueta de propiedad es "Pagado".
assertValueInList(2, 4, "No");Por:
assertValueInList(2, 4, "");Por otra parte si quieres mantener el comportamiento clásico, simplemente añade la siguiente entrada a tu fichero editores.xml:
<editor url="booleanEditor.jsp"> <formateador clase="org.openxava.formatters.BooleanFormatter" > <poner propiedad="nullAsFalse" valor="true"/> </formateador> <formateador-lista clase="org.openxava.formatters.BooleanIconListFormatter" /> <para-tipo tipo="boolean" /> <para-tipo tipo="java.lang.Boolean" /> </editor>Fíjate en el uso de BooleanIconListFormatter como formateador para la lista.
private String [] accionesLista = { "Print.generatePdf", "Print.generateExcel", "ExtendedPrint.myReports", "CRUD.new", "CRUD.deleteSelected", "CRUD.deleteRow", "Mode.detailAndFirst", "Mode.split", "List.filter", "List.orderBy", "List.viewDetail", "List.hideRows", "List.sumColumn", "List.changeConfiguration", "ListFormat.select", "List.changeColumnName", // AÑADE ESTA ENTRADA "ImportData.importData" // AÑADE ESTA ENTRADA }; assertActions(accionesLista);
String [] accionesDespuesNuevo = { "Navigation.previous", "Navigation.first", "Navigation.next", "CRUD.new", "CRUD.save", // "CRUD.delete", // QUITA ESTA LÍNEA "CRUD.refresh", "Mode.list", "Mode.split", "List.filter", "List.orderBy", "Print.generatePdf", "Print.generateExcel" }; assertActions(accionesDespuesNuevo);
String [] acciones = { "Navigation.previous", "Navigation.first", "Navigation.next", "CRUD.new", "CRUD.save", "CRUD.delete", // "CRUD.search", // QUITA ESTA LÍNEA "CRUD.refresh", "Mode.list", "Mode.split", }; assertActions(acciones);Si usas CRUD.search en tus pruebas las puedes cambiar por CRUD.refresh, es decir cambia:
execute("CRUD.search"); setValue("numero", "123"); execute("Search.search");Por:
setValue("numero", "123"); execute("CRUD.refresh");Además, si quieres que la acción de buscar vuelva puedes usar el nuevo controlador SearchForCRUD que contiene únicamente la acción de buscar. Para definir un módulo con la acción buscar en aplicacion.xml añade el controlador SearchForCRUD:
<modulo nombre="Albaran"> <modelo nombre="Albaran"/> <controlador nombre="Typical"/> <controlador nombre="SearchForCRUD"/> <!-- AÑADE ESTA LÍNEA --> </modulo>Otra opción es definirlo en controladores.xml de esta manera:
<controlador nombre="Autor"> <hereda-de controlador="Typical"/> <hereda-de controlador="SearchForCRUD"/> <!-- AÑADE ESTA LÍNEA --> <accion nombre="hazAlgo" clase="com.miempresa.prueba.acciones.HazAlgo" /> </controlador>Incluso si vuelves a añadir la acción de buscar con SearchForCRUD has de modificar tus prueba JUnit porque search ya no está en CRUD sino en SearchForCRUD. Por lo tanto has de cambiar:
execute("CRUD.search");Por:
execute("SearchForCRUD.search");También tenemos un controlador TypicalWithSearch que tiene todas las acciones de Typical más la acción de buscar, igual que con el Typical de toda la vida. Si quieres que se use por defecto escribe el siguiente código al principio de tu aplicacion.xml:
<modulo-defecto> <controlador nombre="TypicalWithSearch"/> </modulo-defecto>
public void testCrearExamenConAlMenosUnaPregunta() throws Exception { assertListRowCount(0); // AHORA ESTA LÍNEA FALLA execute("CRUD.new"); setValue("nombre", "ADMISIÓN"); execute("CRUD.save"); ... }Si el módulo no tiene filas, ahora empieza en modo detalle por tanto la primera línea fallará. Podemos cambiar a modo lista al principio:
public void testCrearExamenConAlMenosUnaPregunta() throws Exception { execute("Mode.list"); // AÑADE ESTA LÍNEA assertListRowCount(0); execute("CRUD.new"); setValue("nombre", "ADMISIÓN"); execute("CRUD.save"); ... }O si tu prueba va directamente al modo detalle, como en este ejemplo, simplemente quita la primera parte de la prueba:
public void testCrearExamenConAlMenosUnaPregunta() throws Exception { // assertListRowCount(0); // QUITA ESTA LÍNEA // execute("CRUD.new"); // QUITA ESTA LÍNEA setValue("nombre", "ADMISIÓN"); execute("CRUD.save"); ... }
private String [] accionesLista = { "Mode.detailAndFirst", "Mode.split", "List.filter", "List.orderBy", "List.viewDetail", "List.hideRows", "List.sumColumn", // "List.changeConfigurationName" // QUITA ESTA ENTRADA "List.changeConfiguration" // AÑADE ESTA ENTRADA }; assertActions(accionesLista);
@Entity public class Factura { private int ano; private int numero; private Date fecha; @ManyToOne private Cliente cliente; @Stereotype("ARCHIVOS") @Column(length=32) private String archivos; ... }En versiones anteriores hubiéramos obtenido: ano, numero, fecha, archivos
@NewAction("Factura.anadirAlbaranes") @OneToMany(mappedBy="factura") private Collection<Albaran> albaranes;Por:
@AddAction("Factura.anadirAlbaranes") @OneToMany(mappedBy="factura") private Collection<Albaran> albaranes;
execute("Collection.view", "row=1,viewObject=xava_view_customers");Por:
execute("Collection.edit", "row=1,viewObject=xava_view_customers");Fíjate que ahora no es "Collection.view" sino "Collection.edit".
assertLabelInList(4, "Nombre de Cliente");Por esto:
assertLabelInList(4, "Cliente");
private String [] accionesLista = { "Print.generatePdf", "Print.generateExcel", // ELIMINA ESTA LÍNEA "ExtendedPrint.myReports", "CRUD.new", "CRUD.deleteSelected", ... }; assertActions(accionesLista);Aunque la acción no esté disponible por defecto, todavía existe en OpenXava y funciona perfectamente. De hecho, hay un nuevo controlador TypicalExtendedPrint para facilitar su uso. Si quieres continuar usando la funcionalidad Mis informes cambia Typical por TypicalExtendedPrint en tu aplicacion.xml y controladores.xml, incluso lo puedes declarar como controlador por defecto.
private String [] accionesLista = { "Mode.detailAndFirst", "Mode.split", "List.filter", "List.orderBy", "List.viewDetail", "List.hideRows", "List.sumColumn", "List.changeConfigurationName" // AÑADE ESTA ENTRADA }; assertActions(accionesLista);
protected void setUp() throws Exception { crearEntidadesUsandoJPA(); // Ahora falla, porque JPA todavía no está inicializado super.setUp(); }Cámbialo por:
protected void setUp() throws Exception { super.setUp(); // JPA se incializa aquí crearEntidadesUsandoJPA(); }
form.getSelectByName("ox_MiAp_MiModulo__miReferencia___id").setSelectedAttribute("ElValor", true);Por:
HtmlInput comboInput = form.getInputByName("ox_MiAp_MiModulo__miReferencia___id"); comboInput.setValueAttribute("ElValor"); ((HtmlInput) comboInput.getPreviousElementSibling()).setValueAttribute("Algo"); // Un truco para evitar que JavaScript borre el valor real ((HtmlInput) comboInput.getNextElementSibling()).setValueAttribute("Algo"); // Un truco para evitar que JavaScript borre el valor real
<accion nombre="borrar" modo="detalle" confirmar="true" clase="com.miempresa.miaplicacion.acciones.MiBorrar" imagen="delete.gif" atajo-de-teclado="Control D"/>Por esto:
<accion nombre="borrar" mode="detalle" confirmar="true" clase="com.miempresa.miaplicacion.acciones.MiBorrar" icono="delete" atajo-de-teclado="Control D"/>Para ver todos los iconos disponibles visita Material Design Icons.
useIconsInsteadOfImages=falseDe esta manera todas las acciones, incluyendo las estándar, usarán las viejas imágenes en color.
<property name="schema.path" value="../OpenXavaTest/lib/hsqldb.jar"/>
Por esto:<property name="schema.path" value="../OpenXava/lib/hsqldb.jar"/>
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {Por:
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor sessionImplementor, Object owner) throws HibernateException, SQLException {Y esto:
public void nullSafeSet(PreparedStatement ps, Object value, int index) throws HibernateException, SQLException {Por:
public void nullSafeSet(PreparedStatement ps, Object value, int index, SessionImplementor sessionImplementor) throws HibernateException, SQLException {Por supuesto, si llamas a estos métodos debes de adaptar la llamada. Por ejemplo, un código como este:
((UserType) hibernateType).nullSafeSet(ps, o, 1);Se quedaría:
((UserType) hibernateType).nullSafeSet(ps, o, 1, null);Fíjate en que enviar un nulo para el nuevo parámetro es suficiente.
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.ejb.cfgfile" value="/hibernate-jpa.cfg.xml"/> <!-- ELIMINA LAS SIGUIENTES LÍNEAS <property name="hibernate.ejb.event.pre-insert" value="org.openxava.hibernate.impl.CalculatorsListener, org.openxava.hibernate.impl.ReferenceConverterToDBListener, org.openxava.hibernate.impl.DefaultValueCalculatorsListener"/> <property name="hibernate.ejb.event.pre-update" value="org.openxava.hibernate.impl.CalculatorsListener, org.openxava.hibernate.impl.ReferenceConverterToDBListener"/> <property name="hibernate.ejb.event.pre-delete" value="org.openxava.hibernate.impl.CalculatorsListener"/> <property name="hibernate.ejb.event.post-load" value="org.openxava.hibernate.impl.CalculatorsListener"/> --> </properties> </persistence-unit>También si usas componentes XML ya no puedes tener una referencia usada como clave si el valor no existe en la tabla referenciada. Este caso ya no está permitido por Hibernate. Este caso nunca ha estado permitido en JPA. Deberías de pensar en una forma diferente de resolver el problema, como añadiendo una clave autogenerada o creando el registro en la tabla referenciada.
<persistence-unit name="default"> ... <properties> ... <!-- Añade la siguiente línea --> <property name="hibernate.connection.release_mode" value="after_transaction"/> </properties> </persistence-unit>
import org.hibernate.validator.*;Por:
import javax.validation.constraints.*; import org.hibernate.validator.constraints.*;En el caso de @Digits aparte de cambiar los susodichos imports tienes que cambiar los nombres de los parámetros, cambiando esto:
@Digits(integerDigits=10, fractionalDigits=6)Por esto:
@Digits(integer=10, fraction=6)Ya no puedes usar InvalidStateException de Hibernate Validator 3. Por lo tanto, si la usas para lanzar un error de validación desde tu entidad u acción tendrás que cambiarla por otra excepción. Por ejemplo, un código como el siguiente:
@PreRemove public void validarAlBorrar() { if (numero == 1) { throw new InvalidStateException( new InvalidValue [] { new InvalidValue( "uno_no_se_puede_borrar", getClass(), "numero", getNumero(), this) } ); } }Ahora podría escribirse de esta manera:
@PreRemove public void validarAlBorrar() { if (numero == 1) { throw new javax.validation.ValidationException("uno_no_se_puede_borrar"); } }Si has definido tu propio validador con el Hibernate Validator antiguo deberías reescribirlo usando el estándar Bean Validation. No te preocupes, la conversión es muy sencilla. En tu anotación cambia @ValidatorClass por @Constraint y añade groups() y payload(), así:
@Constraint(validatedBy = MyValidator.class) // En lugar de @ValidatorClass(PropertyValidatorValidator.class) public @interface MyAnnotation { ... // Añade el siguiente código Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }Y en tu clase validador, cambia esto:
public class RequiredValidator implements Validator<Required> {Por:
public class RequiredValidator implements ConstraintValidator<Required, Object> {Y esto:
public boolean isValid(Object value) {Por:
public boolean isValid(Object value, ConstraintValidatorContext context) {Puedes dejar el resto del código como está.
try { XPersistence.commit(); } catch (RollbackException ex) { if (ex.getCause() instanceof InvalidStateException) { InvalidStateException iex = (InvalidStateException) ex.getCause(); int cantidadValoresInvalidos = iex.getInvalidValues().length; String propiedad = iex.getInvalidValues()[0].getPropertyName(); String mensaje = iex.getInvalidValues()[0].getMessage(); ... } }Deberías escribirlo de esta manera:
try { XPersistence.commit(); } catch (RollbackException ex) { if (ex.getCause() instanceof ConstraintViolationException) { ConstraintViolationException cex = (ConstraintViolationException) ex.getCause(); int cantidadValoresValidos = cex.getConstraintViolations().size(); ConstraintViolation v = cex.getConstraintViolations().iterator().next(); String propiedad = v.getPropertyPath().toString()); String mensaje = v.getMessage()); ... } }
<non-jta-data-source>java:comp/env/jdbc/TuAplicacionDS</non-jta-data-source>Por esto:
<non-jta-data-source>java://comp/env/jdbc/TuAplicacionDS</non-jta-data-source>Y en hibernate.cfg.xml cambia esto:
<property name="hibernate.connection.datasource">java:comp/env/jdbc/TuAplicacionDS</property>Por esto otro:
<property name="hibernate.connection.datasource">java://comp/env/jdbc/TuAplicacionDS</property>Fíjate en // antes de comp. Si usas nombres JNDI en otras partes de tu aplicación también tendrías que cambiarlos. Lo bueno es que las nuevas versiones de Tomcat también funcionan de esta forma, por tanto si usas siempre esta notación tu aplicación funcionará en cualquier Tomcat.
public class PedidoLogic { @Formula("precioProducto * cantidad") public void deriveImporte() { } }Borra la clase de arriba y mueve el cálculo en la anotación @Formula a un método @PrePersist y @PreUpdate en la entidad. Así:
@Entity public class Pedido { ... @PrePersist @PreUpdate private void deriveImporte() { importe = new BigDecimal(cantidad).multiply(precioProducto); } }Además, has de eliminar la siguiente entrada de tu persistence.xml:
<property name="hibernate.current_session_context_class" value="com.autobizlogic.abl.session.LogicThreadLocalSessionContext"/>
<!-- Genera el esquema desde cero. Lo muestra en consola, pero no lo ejecuta --> <target name="generarSchema"> <ant antfile="../OpenXava/build.xml" target="generateSchemaJPA"> <property name="persistence.unit" value="junit"/> <!-- AÑADE LA SIGUIENTE LÍNEA PARA v5.3 --> <property name="schema.path" value="../OpenXavaTest/lib/hsqldb.jar"/> </ant> </target>
Factura factura = (Factura) getView().getEntity(); if (factura.getCliente().getCodigo() == 0) { // En v5.2 si cliente está vacío se devuelve un cliente vacíoPor:
Factura factura = (Factura) getView().getEntity(); if (factura.getCliente() == null) { // En v5.2.1 si cliente está vacío se devuelve nulo
getWebClient().setCssEnabled(true);Por:
getWebClient().getOptions().setCssEnabled(true);Y:
HtmlElement consola = getHtmlPage().getElementById("xava_console");Por:
HtmlElement consola = getHtmlPage().getHtmlElementById("xava_console");Y muchas más cosas. Por desgracia, el equipo de HtmlUnit refactoriza su librería en cada nueva versión menor, por tanto HtmlUnit nunca es compatible hacia atrás.
@DefaultValueCalculator( value=CalculadorPrecioUnitario.class, properties=@PropertyValue( name="numeroProducto", from="producto.numero") ) private BigDecimal precioUnitario;Con versiones anteriores a 5.1 cuando producto.numero cambia precioUnitario se recalcula solo si no tiene valor todavía. Desde la versión 5.1 precioUnitario se recalcula siempre que producto.numero cambia.
@OnChange(RecalcularPreciOUnitarioAlCambiarProducto.class) private Producto producto; private BigDecimal precioUnitario;
autologinUser=admin autologinPassword=adminDe esta manera tus pruebas funcionarán bien sin necesidad de recodificarlas.
public void testMiPrueba() throws Exception { login("admin", "admin"); ... }
<filter> <filter-name>naviox</filter-name> <filter-class>com.openxava.naviox.web.NaviOXFilter</filter-class> </filter> <filter-mapping> <filter-name>naviox</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <servlet-name>naviox</servlet-name> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <servlet-name>module</servlet-name> </filter-mapping>Además cambia:
<servlet> <servlet-name>naviox</servlet-name> <servlet-class>com.openxava.naviox.web.NaviOXServlet</servlet-class> </servlet>Por:
<servlet> <servlet-name>modules</servlet-name> <servlet-class>org.openxava.web.servlets.ModuleServlet</servlet-class> </servlet>Y cambia:
<servlet-mapping> <servlet-name>naviox</servlet-name> <url-pattern>/modules/*</url-pattern> </servlet-mapping>Por:
<servlet-mapping> <servlet-name>modules</servlet-name> <url-pattern>/modules/*</url-pattern> </servlet-mapping>Finalmente quita:
<servlet-mapping> <servlet-name>naviox</servlet-name> <url-pattern>/m/*</url-pattern> </servlet-mapping>Con estos cambios en OpenXava/web.xml has eliminado los menús y la identificación de usuarios. Tienes que ejecutar la tarea ant actualizarOX para actualizar el web.xml en tu proyecto.
styleClass=org.openxava.web.style.Liferay51Style styleCSS=liferay51/css/everything_unpacked.css
<modulo nombre="FiltroPorMes"> <modelo nombre="FiltroPorMes"/> <controlador nombre="FiltroPorMes"/> </modulo>De todas formas, siempre ha sido necesario declarar los módulos para las clases transitorias si queriamos generar portlets, por lo tanto si trabajas con portales ya tendrás los módulos definidos.
javaLoggingLevel=FINESTPor:
javaLoggingLevel=FINESi quieres mantener una cantidad de traza moderada.
Objects.execute(miObjeto, "hacerAlgo");Ahora tienes que escribir en su lugar:
XObjects.execute(miObjeto, "hacerAlgo");
public class MiModuloTest extends ModuleTestBase { ... @Override protected String getModuleURL() { // return super.getModuleURL() + "?miParametro=5"; // Con ? en v4.x.x return super.getModuleURL() + "&miParametro=5"; // Con & en v5.0 } }
@Digits(integer=10) private BigDecimal importe;Con v4.x tiene 2 cifras decimales porque la escala por defecto para BigDecimal es 2. Sin embargo, a partir de v5.0 tiene 0 cifras decimales, porque no especificar fracción es lo mismo que poner fraction=0. Esto te permite tener números BigDecimal con 0 para la parte decimal, pero podría hacer que algunas de tus actuales propiedes numéricas dejen de usar 2 dígitos para la fracción. Para arreglarlo simplemente especifíca la fracción explicitamente, así:
@Digits(integer=10, fraction=2) private BigDecimal importe;
assertValue("miNumeroBigDecimal", "20");por esto:
assertValue("miNumeroBigDecimal", "20.00");
assertPopupPDFLinesCount(5); // Quizás ahora hay 6 líneas, porque la cabecera usa 2 líneas
@View(members= ... "observaciones { consejo, atajo; observaciones }" ... )Donde la propiedad observaciones está dentro de una sección llamada observaciones. Hasta ahora si escribías esto:
getView().setHidden("observaciones", true);Ocultabas la propiedad observaciones, sin embargo desde OpenXava 4.8.1 ocultarás la sección observaciones. En este caso, si aún quieres ocultar la propiedad en lugar de la sección deberías renombrar la sección, como sigue:
@View(members= ... "comentarios { consejo, atajo; observaciones }" ... )
int[] selected = tab.getSelected(); for (int i = 0; i < selected.length; i++){ Map clave = (Map) tab.getTableModel().getObjectAt(selected[i]); .... }por
Map[] selected = tab.getSelectedKeys(); for (int i = 0; i < selected.length; i++){ Map clave = selected[i]; .... }
mi_mensaje=T'ha dit que nopor
mi_mensaje=T''ha dit que noFíjate como hay que cambiar ' por ''.
<input id="<%=propertyKey%>" name="<%=propertyKey%>" class="<%=style.getEditor()%> <%=numericClass%>" type="<%=inputType%>" tabindex="1" <!-- Ahora hay que poner esto --> ... />
baseCondition = "select CODIGO, DESCRIPCION, FAMILIA.DESCRIPCION " + "from XAVATEST.SUBFAMILIA, XAVATEST.FAMILIA " + "where SUBFAMILIA.FAMILIA = FAMILIA.CODIGO"Lo has de reescribir de esta forma:
baseCondition = "select e.codigo, e.descripcion, f.descripcion " + "from Subfamilia e, Familia f " + "where e.codigoFamilia = f.codigo"La sintaxis de JPQL y SQL son muy parecidas, normalmente no es necesario hacer grandes cambios más allá de cambiar los nombres de tablas y columnas por nombres de propiedades y tablas. Has de usar e como alias para la entidad principal.
@DescriptionsList( // No funciona porque IDCOSA es un nombre de columna, por eso JPA no lo reconoce condition="${cosa.codigo} = (SELECT IDCOSA FROM ${Cosa} WHERE nombre = 'COCHE')" )La solución es usar nombres de propiedades en vez de nombres de columnas, en este caso cambiando IDCOSA por codigo es suficiente para resolver el problema:
@DescriptionsList( // Funciona porque usamos el nombre de propiedad, codigo, en vez de IDCOSA condition="${cosa.codigo} = (SELECT codigo FROM ${Cosa} WHERE nombre = 'COCHE')" )Otra opción es escribir la sentencia completa en puro JPQL, dado que los ${} ya no son necesarios, como sigue:
@DescriptionsList( // Podemos usar JPQL puro sin ${nombrePropiedad} condition="e.cosa.codigo = (SELECT c.codigo FROM Cosa c WHERE c.nombre = 'COCHE')" )Si usas JPQL has de usar e como alias para la entidad principal.
public class MiOnSelectElementAction extends OnSelectElementBaseAction { public void execute() throws Exception { hacerAlgoQueModificaLaColeccion(); getView().refreshCollections(); // A partir de v4.5 has de añadir esta linea } }
private String [] accionesLista = { "Mode.detailAndFirst", "Mode.split", "List.filter", "List.customize", "List.orderBy", "List.viewDetail", "List.hideRows", "List.sumColumn" // AÑADE ESTA ENTRADA }; assertActions(accionesLista);
<para-tipo tipo="boolean" clase-conversor="org.openxava.converters.Boolean01Converter" tipo-cmp="java.lang.Integer"/> <para-tipo tipo="java.lang.Boolean" clase-conversor="org.openxava.converters.Boolean01Converter" tipo-cmp="java.lang.Integer"/>Recuerda, añadir estás líneas no suele ser necesario, regenerar código sin más debería ser suficiente. Y si tu aplicación es JPA (el caso más normal) no necesitas hacer nada.
public class Albaran { @Id @Column(length=5) private int numero; @Id @ManyToOne(fetch=FetchType.LAZY) private TipoAlbaran tipo; // El tipo ha de existir en la base de datos ... }En este caso el TipoAlbaran referenciado tiene que existir siempre en la base de datos.
Albaran albaran = new Albaran(); TipoAlbaran tipoAlbaran = new TipoAlbaran(); // Creamos un nuevo TipoAlbaran // en lugar de buscarlo tipoAlbaran.setNumero(66); // El TipoAlbaran 66 ya existe en la base de datos albaran.setTipo(tipoAlbaran); ... XPersistence.getManager().persist(albaran);Pero, esto no funciona con Hibernate 3.6, en su lugar has de escribirlo lo siguiente:
Albaran albaran = new Albaran(); TipoAlbaran tipoAlbaran = XPersistence.getManager() .find(TipoAlbaran.class, 66); // Lo buscamos albaran.setTipo(tipoAlbaran); XPersistence.getManager().persist(albaran);
@Entity @IdClass(PersonaContactoClienteKey.class) public class PersonaContactoCliente { @Id @ManyToOne(fetch=FetchType.LAZY) private Cliente cliente; // No más campos @Id ... }Y la referencia de esta forma:
@ManyToOne(fetch=FetchType.LAZY) @JoinColumn(name="CUSTOMERCONTACT") // Sin el atributo referencedColumnName private PersonaContactoCliente personaContactoCliente;Y esto funcionaba. Sin embargo, con Hibernate 3.6 esto no funciona. Afortunadamente, hay una forma de conseguir el mismo efecto. Escribe tu entidad de la siguiente forma:
@Entity // @IdClass ya no se usa public class PersonaContactoCliente implements java.io.Serializable { // Tiene que implementar Serializable @Id @ManyToOne(fetch=FetchType.LAZY) private Cliente cliente; // No más campos @Id ... }Y la referencia de esta forma:
@ManyToOne(fetch=FetchType.LAZY) @JoinColumns({ @JoinColumn(name="CUSTOMERCONTACT", referencedColumnName="CUSTOMER_NUMBER") // referencedColumnName es obligatorio }) private PersonaContactoCliente personaContactoCliente;
@Entity @Table(name = "`TABLAS`") @DiscriminatorColumn(name="ID", discriminatorType=DiscriminatorType.STRING, length=20)Hibernate 3.4 correrá sin problemas, pero la versión 3.6 (y me parece que la 3.5 también) les arrojará el error de campo duplicado. Para corregir esto debe utilizar una anotación dependiente de Hibernate @DiscriminatorOptions de la siguiente forma:
@Entity @Table(name = "`TABLES`") @DiscriminatorColumn(name="ID", discriminatorType=DiscriminatorType.STRING, length=20) @DiscriminatorOptions(insert = false) // Anotacion dependiente de HibernateY debe funcionar correctamente.
String host = getProperty("host"); // Hasta v4m5 // Tiene que cambiarse por String host = getXavaJUnitProperty("host"); // A partir de v4m6Este cambio fue necesario para permitir usar Groovy al escribir pruebas JUnit.
<modulo nombre="CambiarPrecioProducto"> <modelo nombre="Producto"/> <controlador nombre="Navigation"/> <!-- Has de añadir esto --> <controlador nombre="CambiarPrecioProducto"/> </modulo>
String [] listActions = { "Print.generatePdf", "Print.generateExcel", "CRUD.new", "CRUD.deleteSelected", "Mode.detailAndFirst", "Mode.split", // HAS DE AÑADIR ESTO "List.filter", "List.customize", "List.orderBy", "List.viewDetail", "List.hideRows", }; assertActions(listActions);Si no te gusta este nuevo modo split, lo puedes quitar de tu applicación añadiendo la siguiente entrada en xava.properties:
defaultModeController=DetailListEn este caso el controlador de modo ya no es Mode sino DetailList, esto implica que has de cambiar tu pruebas junit:
// execute("Mode.list"); // NO si defaultModeController=DetailList en xava.properties execute("DetailList.list"); // SÍ si defaultModeController=DetailList en xava.properties
// execute("CRUD.deleteSelected", "row=2"); // NO, desde 4m5 execute("CRUD.deleteRow", "row=2"); // SIAdemás, dado que CRUD.deleteRow está disponible por defecto en todos lo módulos, si usas assertActions() en tus pruebas puede que tengas que añadirla a la lista de acciones.
public class MiAccionBuscar extends ViewBaseAction { public void execute() throws Exception { ... getView().setKeyEditable(false); // Has de llamar a setKeyEditable() getView().setEditable(true); // y setEditalbe() explicitamente ... } }