Las Relaciones
La POO en Java había padecido por mucho tiempo de una manera simple de relacionar las Bases de Datos con los Objetos de POO (Entidades); tras un largo camino en el que felizmente nos encontramos con JDBC, digo 'felizmente' porque verdaderamente fue de gran ayuda tener los datos en un resultSet; posteriormente los EJB con el J2EE y la aparición de Enterprise JavaBeans Query Language (EJB QL) ... los programadores, perdidos en la complejidad de técnicas, librerías y frameworks, necesitaban algo más,..
Finalmente apareció la Java Persistence API, para manejar el Modelo de Objetos y el Modelo Relacional con la técnica conocidad como O-R Mapping, o abreviada como ORM; ... Solamente tenemos que buscar "jpa tutoriales" o algo por el estilo para accessar a un buen de información al respecto. Yo utilizo Netbeans como mi IDE y me gusta mucho, vean aquí Netbeans Tutorial para dejar el punto aquí y entrar al Código.
Estoy creando un pequeño sistema y necesito entender el tema de la Relaciones entre tablas a mayor profundidad, por eso escribo esto.
Bien, partamos de un projecto al que llamo 'elGustoJPA', ....
Vamos poco a poco...
Una Relación Sencilla (y común)
Defino una base de Datos con dos tablas, para empezar. El suspuesto es que necesito una tabla Cuenta con una Llave CuentaID grande, porque es mi Tabla Principal, en la que manejo muchas cuentas, pero la tabla de Clientes tiene una Llave Compuesta por ClienteID y CuentaID.
Cuentas:
CREATE TABLE `elgusto`.`cuenta` (
`CuentaID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`Nombre` VARCHAR(45) NULL ,
PRIMARY KEY (`CuentaID`) );
CREATE TABLE `elgusto`.`cliente` (`ClienteID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`CuentaID` BIGINT(20) NOT NULL,
`Nombre` VARCHAR(45) NULL,
PRIMARY KEY (`ClienteID`, `CuentaID`) ,
INDEX `cta_idx` (`CuentaID` ASC) ,
CONSTRAINT `cta`
FOREIGN KEY (`CuentaID`)
REFERENCES `elgusto`.`cuenta` (`CuentaID`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
| fig 1.- Cuenta - Cliente |
En mi proyecto de Netbean, vamos a utilizar el generador para crear las Entities para ésta Tabla, y vemos que me genera la siguiente estructura:
Veamos la Clase Cliente.
package Entities;
import ...;
@Entity
@Table(name = "cliente")
@NamedQueries({...})
public class Cliente implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
protected ClientePK clientePK;
@Size(max = 45)
@Column(name = "Nombre")
private String nombre;
@JoinColumn(name = "CuentaID", referencedColumnName = "CuentaID", insertable = false, updatable = false)
@ManyToOne(optional = false)
private Cuenta cuenta;
public Cliente() {}
public Cliente(ClientePK clientePK) {
this.clientePK = clientePK;
}
public Cliente(long clienteID, long cuentaID) {
this.clientePK = new ClientePK(clienteID, cuentaID);
}
public ClientePK getClientePK() {
return clientePK;
}
public void setClientePK(ClientePK clientePK) {
this.clientePK = clientePK;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public Cuenta getCuenta() {
return cuenta;
}
public void setCuenta(Cuenta cuenta) {
this.cuenta = cuenta;
}
...
}
Podemos ver que aparece una Anotación @EmbeddedId asociada con un Objeto de otra de las Clases ClientePK, y que los Constructores de Cliente, además del vacio ({}), reciben un Objeto ClientePK o dos valores de tipo Long, clienteID y cuentaID con los que crea un Objeto ClientePK.
Veamos ahora la Clase ClientePK:
package Entities;
import ...;
@Embeddable
public class ClientePK implements Serializable {
@Basic(optional = false)
@Column(name = "ClienteID")
private long clienteID;
@Basic(optional = false)
@NotNull
@Column(name = "CuentaID")
private long cuentaID;
public ClientePK() {}
public ClientePK(long clienteID, long cuentaID) {
this.clienteID = clienteID;
this.cuentaID = cuentaID;
}
public long getClienteID() {
return clienteID;
}
public void setClienteID(long clienteID) {
this.clienteID = clienteID;
}
public long getCuentaID() {
return cuentaID;
}
public void setCuentaID(long cuentaID) {
this.cuentaID = cuentaID;
}
...
}
Esta es una Clase Auxiliar para poder manejar una Llave Compuesta. Podemos identificar primeramente que está Anotado como una Clase Incrustable (@Embeddable) que en Cliente se agrega mediante un Objeto de ésta Clase pero Anotado con @EmbeddedId para indicar que es la Llave de la Clase.
Encontramos además una Referencia a un Objeto de Tipo Cuenta, de la siguiente manera:
@JoinColumn(name = "CuentaID", referencedColumnName = "CuentaID", insertable = false, updatable = false)
@ManyToOne(optional = false)
private Cuenta cuenta;
Que maneja la Referencia a la Tabla Cuenta, por medio de la Columna "CuentaID", con dos Anotaciones: @JoinColumn y @ManyToOne.
Hasta ahora, no hemos metido las manos realmente.
¿Qué pasa si queremos agregar las JSF con PrimeFaces para generar las pantallas de Captura?
Veamos.
Que nos genera la siguiente estructura:
Veamos que la Carpeta 'JSF' contiene otras dos, una por cada Tabla y cada una de ellas cuatros archivos con extensión .xhtml. Esos son los archivos que necesitamos.
Lo ejecutamos...
y tenemos que crear un par de Cuentas:
Y ahora los Clientes..
Listo!, si hago una consulta de mi BD, encuentro:
Y
Perfecto! ;)