Entidades
Introducción
Se trata en este capítulo de crear las clases para las entidades ModoPagos, Cliente, Articulo y Pedido que representan el modelo de dominio principal resultante del análisis del proyecto.
Las clases de entidades tienen en su mayoría tablas correspondientes en la base de datos.
Las entidades tienen dos vertientes funcionales: una describe los datos a manipular, la otra las operaciones que permiten su interacción con la base de datos.
Este segundo aspecto concierne principalmente a las operaciones CRUD (Create, Read, Update, Delete, es decir Crear, Leer, Modificar, Eliminar) que permiten respectivamente realizar operaciones de creación, lectura, actualización y eliminación de registros de la base de datos. A lo largo de este capítulo se podrá ver cómo codificarlas poco a poco.
Es tentador mezclar las operaciones CRUD en el interior de la clase de datos. Esto plantea varios problemas: las responsabilidades funcionales se mezclan entonces; el código resultante se encuentra muchas veces con artificios técnicos; el aspecto pedagógico se diluye y ¡el objetivo de este libro es ante todo pedagógico!
Gestión de los errores
Una gran parte de las actividades de desarrollo de una aplicación es la gestión de los errores que acontecen a lo largo de las actividades de esta aplicación.
En lo referente a la base de datos, estos errores tomarán la forma de excepciones lanzadas por JPA cuando se llame a los métodos de la clase EntityManager. Estos errores heredan de la clase RuntimeException, no forman por lo tanto parte de la firma de la clase, pero son aun así susceptibles de ocurrir. En vez de gestionarlos a través de un bloque try/catch en esta parte de código, un capítulo posterior explicará cómo será responsabilidad de los métodos que las llaman el gestionarlos.
Esta manera de trabajar simplifica la legibilidad del código, y contribuye a una mejor separación de las tareas: ¡no es normal para una clase técnica mostrar cuadros de diálogo informando de estos errores!
Tenga en mente que pueden ocurrir excepciones en la ejecución de estos métodos.
Clase ModoPagos
Se empieza por la clase ModoPagos. Es la más sencilla de nuestras modelizaciones, y está en el corazón de la aplicación. ¡Después de todo, se trata de una aplicación de gestión de pedidos, y sin pagos esta aplicación no serviría de nada en una empresa privada!
Aquí tiene el código de esta clase, presente en el package entidad:
package entidad;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Transient;
@Entity
public class ModoPagos implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private int codigo;
@Basic
private String tipo;
@Transient
private transient boolean ignorado;
/*
* Constructor 1.
* Utilizado por JPA.
*/
public...
CRUD de la clase ModoPagos
La clase CRUD del modo de pago se llama ModoPagosCrud en este proyecto.
Cree un package crud en el package entidad.
Cree la clase ModoPagosCrud en este package.
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.ModoPagos;
public class ModoPagosCrud {
/**
*
*/
private final Conexion laConexion;
/**
* Constructor
*/
public ModoPagosCrud(Conexion unaConexion) {
laConexion = unaConexion;
}
}
Esta clase posee un constructor con un parámetro: la instancia de Conexion. Esto evita utilizar métodos estáticos durante el ciclo de vida de la aplicación y mejora las posibilidades de test unitarios de esta clase.
El CRUD debe poder crear, leer, modificar y eliminar una línea de datos. Se añadirá una posibilidad de búsqueda a este conjunto de funcionalidades.
1. Crear
Cree un nuevo método en la clase.
/**
* Añadir un nuevo elemento en la base de datos.
*
* @param tipo
* el nombre textual del modo de pago.
* @return el modo de pago creado
* o null si no se puede crear.
*/
public ModoPagos crear(String tipo) {
return laConexion.modificar((administrador) -> {
if ("".equals(tipo)) {
throw new IllegalArgumentException(
"El nombre del modo de pago es obligatorio");
} else {
ModoPagos pagos = new ModoPagos(tipo);
...
Clase Cliente
Aquí tiene el código de esta clase:
package entidad;
import java.io.Serializable;
import java.time.Instant;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
public class Cliente implements Serializable {
private static final long serialVersionUID = 1L;
// Propriedades básicas de la clase
// --------------------------------
// el identificador de la entidad
@Id
private String codigo;
@Basic
private String apellido;
private String nombre;
private boolean tarjeta_fidelidad;
@Temporal(TemporalType.DATE)
private Date fecha;
@OneToOne(cascade=CascadeType.ALL)
private Direccion direccion;
// CONSTRUCTORES
// -------------
// 1er constructor
// para la creación completa de un cliente
// limitado aquí...
CRUD de la clase Cliente
Ahora es momento de crear la clase de acceso a los clientes.
Cree la clase ClienteCrud en el package “crud”.
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.Cliente;
public class ClienteCrud {
// Propriedad para establecer la conexion con la BD
// ------------------------------------------------
private final Conexion laConexion;
// CONSTRUCTOR
// -------------
public ClienteCrud(Conexion conexion) {
this.laConexion = conexion;
}
}
El constructor de esta clase sigue el principio de la clase ModoPagosCrud.
1. Crear
Cree un nuevo método en la clase.
// Añadir un nuevo cliente en la BD
// --------------------------------
public void crear(Cliente cliente) {
laConexion.modificar((administrador) -> {
administrador.persist(cliente);
return cliente;
});
}
A diferencia de ModoPagosCrud, este método recibe un cliente completo como parámetro, y no devuelve nada. Si termina con normalidad, se ha guardado el cliente en la base de datos. Si la operación no se ha podido realizar, se lanzará una excepción de tipo RuntimeException por JPA, interrumpiendo el hilo normal de ejecución.
2. Leer
Cree un nuevo método en la clase.
/*
* Lectura y recuperación de registros de la BD
*/
public List<Cliente> leer() {
return laConexion.buscar((administrador) -> {
Query query = administrador.createQuery("SELECT c
FROM Cliente c");
return query.getResultList();
});
}
Este método enumera todos...
Clase Articulo
Continúe creando la clase Articulo en el package entidad.
package entidad;
import java.io.Serializable;
import java.time.Instant;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
public class Articulo implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String codigo;
@ManyToOne(cascade = { CascadeType.PERSIST })
private Categoria categoria;
@ManyToMany(fetch = FetchType.EAGER)
private Set<Proveedor> proveedores = new HashSet<>();
private String designacion;
private int cantidad;
@Column(name="precio_unitario")
private double precioUnitario;
@Temporal(TemporalType.DATE)
private Date fecha;
/*
* Constructor
*/
public Articulo(String codigo, String codigoCategoria,
String designacion,
int cantidad, double precioUnitario,
...
CRUD de la clase Articulo
Cree ahora la clase de acceso a los artículos.
Cree una clase ArticuloCrud en el package entidad.crud.
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.Articulo;
import entidad.Categoria;
public class ArticuloCrud {
private final Conexion laConexion;
/*
* Constructor
*/
public ArticuloCrud(Conexion conexion) {
this.laConexion = conexion;
}
}
1. Crear
Cree un nuevo método en la clase.
/**
* Añadir un nuevo artículo en la BD.
*
* @throws IllegalArgumentException
* si resulta imposible crear el artículo.
*/
public void crear(Articulo articulo) {
laConexion.modificar((administrador) -> {
administrador.persist(articulo);
return articulo;
});
}
La novedad de este método respecto a los métodos crear anteriores es indicar en JavaDoc que puede producirse una excepción durante su ejecución.
Se trata de un indicador adicional para el que use este método. ¡Sin embargo esta indicación supone que el desarrollador lee esta documentación para tenerla en cuenta!
2. Leer
...
Clase Pedido
Ahora toca codificar las clases Pedido y Linea: cada instancia de la clase Linea corresponde a una línea de compra en un pedido.
Cree una clase Linea en el package entidad.
package entidad;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity(name="LineasPedidos")
public class Linea implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private String id;
@ManyToOne(optional=false)
private Articulo articulo;
private int cantidad;
/*
* Constructor 1
*/
public Linea(Articulo unArticulo, int cantidad) {
this();
this.articulo = unArticulo;
this.cantidad = cantidad;
}
public Linea() {
super();
}
public Articulo getArticulo() {
return articulo;
}
public void setArticulo(Articulo unArticulo) {
this.articulo = unArticulo;
}
public int getCantidad() {
return cantidad;
}
public void setCantidad(int cantidad) {
this.cantidad = cantidad;
}
}
La anotación @Entity se parametriza con un nombre, lo que permite cambiar el nombre por defecto de la tabla: en vez de llamarse LINEA, se llamará LINEASPEDIDOS.
La anotación @ManyToOne se parametriza con optional=false para producir un error...
CRUD de la clase Pedido
Cree una clase PedidoCrud en el package entidad.crud.
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.Pedido;
public class PedidoCrud {
private Conexion laConexion;
/*
* Constructor 1
*/
public PedidoCrud(Conexion conexion) {
this.laConexion = conexion;
}
}
1. Crear
Cree un nuevo método en la clase.
/**
* Añadir un nuevo pedido en la BD.
* @throws EniException si es imposible crear el pedido.
*/
public void crear(Pedido pedido) throws EniException {
try {
laConexion.modificar((administrador) -> {
admnistrador.persist(pedido);
return pedido;
});
} catch(RuntimeException e) {
throw new EniException(e.getMessage(), e);
}
}
Este método tiene la particularidad de recuperar y transformar la excepción RuntimeException, que puede que JPA lance, en una excepción específica, que no es de tipo RuntimeException. Esta última debe por lo tanto obligatoriamente indicarse en la firma del método con la palabra clave throws. A partir de aquí, todo código que invoque a este método debe explícitamente gestionar esta excepción, en caso contrario el código no compilará.
Se trata de una técnica eficaz para no arriesgarse a olvidar la gestión de un eventual error durante la ejecución de la aplicación.
Sin embargo se debe crear esta excepción específica.
Cree una clase EniException en el package entidad.
package entidad.crud;
public...
Gestión de las entidades por JPA
La codificación de entidades y de sus clases CRUD está casi terminada.
Las clases Categoria y Proveedor y sus CRUD asociados todavía no se han abordado. Le dejamos este trabajo como ejercicio.
Una vez codificadas estas clases, queda indicar a JPA que estas clases deben tenerse en cuenta en las consultas hacia la base de datos.
Abra el archivo persistence.xml. Puede utilizar el atajo de teclado [Crtl][Shift] R para abrir un cuadro de diálogo de búsqueda rápida. Empiece a teclear el nombre del archivo en la zona de búsqueda. Se escaneará el espacio de trabajo con el fin de mostrar los archivos correspondientes. Haga doble clic en el archivo deseado.
Modifique el archivo insertando elementos XML class:
Guarde el archivo pulsando [Ctrl] S.