¡Esta es una revisión vieja del documento!


Objetos y Validaciones

En esta unidad vamos a realizar ejercicios relativos a las validaciones.

Ejercicio01

Siguiendo el Ejercicio01 de la unidad anterior , pero eliminado todo el código relativo a las consultas, vamos a realizar las siguientes tareas.

Validaciones

Las siguientes validaciones seguirán es estándar JSR 303: Bean Validation:

NIF

En la clase NIF

  • Valida que el NIF tenga un tamaño de 10 caracteres.
  • Valida que el NIF siga la expresión regular : ”[XYZ0-9][0-9]{7}[TRWAGMYFPDXBNJZSQVHLCKE]
  • Haz un método que valide la letra del NIF y cuyo mensaje sea “La letra del NIF no es válida”.Usa el siguiente algoritmo:
  • String letras = "TRWAGMYFPDXBNJZSQVHLCKE";
    int valor;
     
    if (nif.startsWith("X")) {
        //Es un NIE
        valor=Integer.parseInt(nif.substring(1,nif.length()-1));
    }else if (nif.startsWith("Y")) {
        //Es un NIE
        valor=10000000+Integer.parseInt(nif.substring(1,nif.length()-1));
    }else if (nif.startsWith("Z")) {
        //Es un NIE
        valor=20000000+Integer.parseInt(nif.substring(1,nif.length()-1));
    } else {
        //Es un NIF
        valor=Integer.parseInt(nif.substring(0,nif.length()-1));
    }
     
    String letraCorrecta=""+letras.charAt(valor % 23);
     
    if (nif.endsWith(letraCorrecta)==true) {
        //El NIF es correcto
    } else {
        //El NIF es erroneo
    }

CodigoCuentaCliente

En la clase CodigoCuentaCliente:

  • Valida que el Codigo Cuenta Cliente tenga un tamaño de 20 caracteres.
  • Valida que el Codigo Cuenta Cliente siga la expresión regular : ”[0-9]{20}
  • Haz un método que valide si el DC de CodigoCuentaCliente es correcto.Haz que el mensaje sea “El DC del Codigo Cuenta Cliente no es correcto”. La siguiente función calcula el valor correcto:
  • private String getDCCorrecto(String banco,String sucursal,String cuenta) {
        int dc1=0;
     
        dc1=dc1+Integer.parseInt(banco.substring(0,1))*4;
        dc1=dc1+Integer.parseInt(banco.substring(1,2))*8;
        dc1=dc1+Integer.parseInt(banco.substring(2,3))*5;
        dc1=dc1+Integer.parseInt(banco.substring(3,4))*10;
        dc1=dc1+Integer.parseInt(sucursal.substring(0,1))*9;
        dc1=dc1+Integer.parseInt(sucursal.substring(1,2))*7;
        dc1=dc1+Integer.parseInt(sucursal.substring(2,3))*3;
        dc1=dc1+Integer.parseInt(sucursal.substring(3,4))*6;
     
        dc1=dc1 %11;
        dc1=11-dc1;
        if (dc1==10) {
            dc1=1;
        }
        if (dc1==11) {
            dc1=0;
        }
     
        int dc2=0;
     
        dc2=dc2+Integer.parseInt(cuenta.substring(0,1))*1;
        dc2=dc2+Integer.parseInt(cuenta.substring(1,2))*2;
        dc2=dc2+Integer.parseInt(cuenta.substring(2,3))*4;
        dc2=dc2+Integer.parseInt(cuenta.substring(3,4))*8;
        dc2=dc2+Integer.parseInt(cuenta.substring(4,5))*5;
        dc2=dc2+Integer.parseInt(cuenta.substring(5,6))*10;
        dc2=dc2+Integer.parseInt(cuenta.substring(6,7))*9;
        dc2=dc2+Integer.parseInt(cuenta.substring(7,8))*7;
        dc2=dc2+Integer.parseInt(cuenta.substring(8,9))*3;
        dc2=dc2+Integer.parseInt(cuenta.substring(9,10))*6;
     
     
        dc2=dc2 %11;
        dc2=11-dc2;
        if (dc2==10) {
            dc2=1;
        }
        if (dc2==11) {
            dc2=0;
        }
     
        String dc=String.valueOf(dc1)+String.valueOf(dc2);
     
        return dc;
    }

Domicilio

En la clase Domicilio:

  • El campo tipoVia no puede estar vacio
  • El campo nombreVia no puede estar vacio
  • El campo poblacion no puede estar vacio
  • El campo provincia no puede estar vacio
  • El campo codigoPostal no puede estar vacio
  • El campo codigoPostal debe seguir la expresión regular ”[0-9]{5}

Cliente

En la clase Cliente:

  • El campo nombre no puede estar vacio
  • El campo ape1 no puede estar vacio
  • El campo fechaNacimiento no puede estar vacio
  • El campo fechaNacimiento debe ser menor que hoy
  • El campo nif debe ser válido
  • El campo domicilio debe ser válido
  • El campo cuentas debe ser válido
  • El campo correoElectronico debe tener el formato de un correo electrónico
  • El campo telefonoFijo debe seguir la expresión regular ”[0-9]{9}
  • El campo telefonoMovil debe seguir la expresión regular ”[0-9]{9}
  • El campo nif debe ser único

Cuenta

En la clase Cuenta:

  • El campo ccc debe ser válido
  • El campo saldo debe tener 10 dígitos enteros y 2 decimales.
  • El campo titulares debe ser válido
  • El campo movimientos debe ser válido
  • El campo ccc debe ser único

Movimiento

En la clase Movimiento:

  • El campo tipoMovimiento no puede estar vacio.
  • El campo importe debe tener 10 dígitos enteros y 2 decimales.

Todas

En todas las clases:

  • Personaliza los mensajes de la anotación @NotNull para que en vez de ser “no puede ser null” sea “No puede estar vacío”.

Cambios en los métodos

Hasta ahora todas nuestras clases han tenido únicamente métodos de set/get y los métodos de validación. Pero ésto nos puede llevar al antipatrón del modelo anémico. Con este antipatrón lo que hacemos es tener dominios sin lógica y toda ella se pone en una capa superior llamada “Capa de servicio (Service Layer)”.

Ya hemos cambiado un poco nuestro modelo para hacerlo menos anémico al añadirle todas las validaciones pero vayamos ahora más allá y organicemos los métodos de las clases para incluir más lógica en nuestras clases.

Todas

En todas las clases:

  • No debería poder establecerse la clave primaria de ninguna clase. Es decir, si ya la genera Hibernate , ¿para qué hay un método setId....?. Debemos eliminarlos todos.Dejamos los getId... ya que son útiles para saber cuál es la clave primaria.
    Al quitar estos métodos se generará un error de que no los encuentra. La solución a este problema es hacer que Hibernate acceda directamente a las propiedades sin usar los métodos get/set ( Usar el atributo access=“field” que se explica en Fichero de mapeo ''.hbm.xml'' ). Ésto lo deberemos hacer en todas las propiedades de todos nuestras entidades ya que así seremos libres de quitar los get/set que queramos.

NIF

En la clase NIF:

  • Eliminar el método setNIF ya que no es necesario puesto que se asigna con el constructor.
  • Añadir un método llamado getNumero() que nos retorna sólo el número del NIF
  • Añadir un método llamado getLetra() que nos retorna sólo la letra del NIF
  • Añadir un método llamado boolean isNIE() que nos retorne true si el NIF es un NIE.

CodigoCuentaCliente

En la clase CodigoCuentaCliente

  • Eliminar el método setCcc ya que no es necesario puesto que se asigna con el constructor.
  • Añadir un método llamado getBanco() que nos retorna el código del banco. ccc.substring(0,4)
  • Añadir un método llamado getSucursal() que nos retorna el código del banco. ccc.substring(4, 8)
  • Añadir un método llamado getCuenta que nos retorna el código del banco. ccc.substring(10, 20)
  • Añadir un método llamado getDC() que nos retorna el código del banco. ccc.substring(8, 10)

Vemos cómo hemos alimentado 1) a las clases NIF y CodigoCuentaCliente que aparentemente no eran muy útiles hasta ahora. Es decir, hemos añadido lógica a nuestro modelo.

Cliente

La siguiente tarea a realizar es la de modificar el saldo de la cuenta a medida que se añaden Movimientos.

  • Eliminar el método setSaldo ya que ahora se modificará internamente al añadir los movimientos.
  • Vamos a hacer que los movimientos sólo se puedan añadir y ver pero nunca modificar o borrar. Para ello haremos lo siguientes cambios:
    • En el método getMovimientos() retornar una lista que no se puede modificar usando el método Collections.unmodifiableList(movimientos)
    • Borrar el método setMovimientos()
    • Añadir el método void addMovimiento(Movimiento movimiento) en el que añadiremos el movimiento a la lista pero tambien modificaremos el valor del saldo , sumando o restando el valor del importe al saldo según la propiedad tipoMovimiento.

Movimiento

En la clase Movimiento

  • Añadir un listener en el evento PreInsert para que se establezca automáticamente el valor de hoy en el campo fecha .
  • Eliminar todos los métodos set... de la clase Movimiento para evitar que se pueda cambiar algo de un movimiento.

Con todo ésto ya hemos finalizado el cambio del modelo para hacerlo mucho menos anémico.

Main

En la clase Main deberemos añadir este código que contiene los cambios en los movimientos.

Date fechaNacimiento = (new GregorianCalendar(1980, 8, 1)).getTime();
 
Cliente cliente1 = new Cliente("Carlos", "Valero", "Giner", fechaNacimiento, new NIF("56765456F"), "963767876", null, "cvalero@gmail.com", null, new Domicilio(TipoVia.Plaza, "Plaza del Ayuntamiento", "12", "6", "Valencia", "Valencia", "46178"));
Cliente cliente2 = new Cliente("Pilar", "Peris", "Cantó", fechaNacimiento, new NIF("47678765H"), "963345656", null, "pperis@gmail.com", null, new Domicilio(TipoVia.Calle, "Calle Garrido", "6", "23", "Cheste", "Valencia", "46896"));
Cuenta cuenta = new Cuenta(new CodigoCuentaCliente("57651209916549873456"), new BigDecimal("1000"));
cuenta.getTitulares().add(cliente1);
cuenta.getTitulares().add(cliente2);
cuenta.addMovimiento(new Movimiento(TipoMovimiento.Debe, new BigDecimal("100"), new Date(), "Compra en la carnicería"));
cuenta.addMovimiento(new Movimiento(TipoMovimiento.Haber, new BigDecimal("20"), new Date(), "Devolución de los pañales en el supermercado"));
try {
    session.beginTransaction();
 
    session.save(cuenta);
 
    session.getTransaction().commit();
} catch (javax.validation.ConstraintViolationException cve) {
    session.getTransaction().rollback();
    System.out.println("No se ha podido insertar la cuenta debido a los siguientes errores:");
    for (ConstraintViolation constraintViolation : cve.getConstraintViolations()) {
        System.out.println("En el campo '" + constraintViolation.getPropertyPath() + "':" + constraintViolation.getMessage());
    }
} catch (org.hibernate.exception.ConstraintViolationException cve) {
    session.getTransaction().rollback();
    System.out.println("No se ha podido insertar la cuenta debido al siguiente error:");
    System.out.println("El valor ya existe." + cve.getLocalizedMessage());
}

Al ejecutar el código podremos ver en la base de datos cómo el saldo de la cuenta 57651209916549873456 es de 920€

Ejercicio02 Optativo

Este ejercicio es optativo.

  • Modifica el Ejercicio01 de esta misma unidad para hacer que al implementar el listener en la clase Movimiento no se tenga ninguna referencia a clases , interfaces ,etc de Hibernate. Es decir que no haya referencia al interfaz PreInsertEventListener ni a nada más de Hibernate.

Ejercicio03 Optativo

Este ejercicio es optativo.

Modifica el Ejercicio01 de esta misma unidad para hacer que se puedan obtener todos los clientes de forma paginada para ello deberás modificar la clase DAO.

Puedes utilizar las clases que prefieras pero una posible solución sería la siguiente: PlantUML Graph

1) lo contrario de estar anémico
ejercicios/unidad06.1346877284.txt.gz · Última modificación: 2016/07/03 20:16 (editor externo)
Ir hasta arriba
CC Attribution-Noncommercial-Share Alike 3.0 Unported
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0