JPA Composite Key y business objects

El uso de JPA composite key es muy común cuando trabajamos con JPA , pronto aparecerán tablas en el modelo de datos que necesitan crear este tipo de claves. Vamos a construir un ejemplo  de como crear una clave compuesta utilizando JPA. Para ello vamos a partir del concepto de Curso que contiene los siguientes campos:

  1. Titulo
  2. Nivel
  3. Categoria
  4. Horas

En un primer lugar  podemos pensar que con el título del Curso es suficiente . Sin embargo pueden existir en el modelo cursos con el mismo nombre pero diferente nivel.  Así pues la clave primaria del curso estará compuesta por titulo y nivel.

JPA Composite key y Cursos

Vamos a declarar dos clases a nivel de Java (Curso y CursoPK) .

JPA Composite Key

La clase CursoPK se va a encargar de almacenar la clave primaria compuesta (titulo y el nivel).


package com.arquitecturajava;

import java.io.Serializable;

import javax.persistence.Embeddable;

@Embeddable
public class CursoPK implements Serializable {

	private String titulo;
	private int nivel;
	public String getTitulo() {
		return titulo;
	}
	public void setTitulo(String titulo) {
		this.titulo = titulo;
	}
	public int getNivel() {
		return nivel;
	}
	public void setNivel(int nivel) {
		this.nivel = nivel;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + nivel;
		result = prime * result + ((titulo == null) ? 0 : titulo.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		CursoPK other = (CursoPK) obj;
		if (nivel != other.nivel)
			return false;
		if (titulo == null) {
			if (other.titulo != null)
				return false;
		} else if (!titulo.equals(other.titulo))
			return false;
		return true;
	}
	public CursoPK(String titulo, int nivel) {
		super();
		this.titulo = titulo;
		this.nivel = nivel;
	}
	
}

Esta clase lleva la anotación @Embeddable que nos permite usarla  dentro de otra. Esa otra clase va a ser la clase Curso que será el objeto de negocio con el que trabajamos.


package com.arquitecturajava;

import java.io.Serializable;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

@Entity
public class Curso implements Serializable {
	private String categoria;
        private int horas;
	
	@EmbeddedId
	private CursoPK cursoPK;
	
	
	public Curso(String titulo,String categoria, int horas , int nivel) {
		super();
		this.categoria = categoria;
		this.horas = horas;
		cursoPK= new CursoPK(titulo,nivel);
	}
	public String getTitulo() {
		return cursoPK.getTitulo();
	}
	public void setTitulo(String titulo) {
		cursoPK.setTitulo(titulo);
	}
	public int getNivel() {
		return cursoPK.getNivel();
	}
	public void setNivel(int nivel) {
		cursoPK.setNivel(nivel);
	}

	
	public String getCategoria() {
		return categoria;
	}
	public void setCategoria(String categoria) {
		this.categoria = categoria;
	}
	public int getHoras() {
		return horas;
	}
	public void setHoras(int horas) {
		this.horas = horas;
	}
	public int hashCode() {
		return cursoPK.hashCode();
	}
	public boolean equals(Object obj) {
		return cursoPK.equals(obj);
	}
	
	
}

La anotación de @EmbeddedId es la encargada de definir que la clase embebida es la que se usa como primary key de la tabla . En este caso al tener CursoPK varios campos la clave es compuesta . Nosotros ademas hemos usado el concepto de delegación a la hora de relacionar la clase Curso y CursoPK diseñando campos específicos que delegan de una clase a la otra. Acabamos de terminar de configurar un JPA composite key.

 

 

Nos queda definir el fichero de persistence.xml para poder salvar nuestros datos.



<persistence xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
 version="2.0">

 <persistence-unit name="arquitecturajava">
 
 <properties>
 <property name = "hibernate.show_sql" value = "true" />
 <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
 <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
 <property name="javax.persistence.jdbc.user" value="root" />
 <property name="javax.persistence.jdbc.password" value=“miclave” />
 <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:8889/java" />
 </properties>
 </persistence-unit>

</persistence>


Por último creamos el programa principal e insertamos :


package com.arquitecturajava;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class Principal {

	public static void main(String[] args) {
		
		
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("arquitecturajava");
		EntityManager em = emf.createEntityManager();
		EntityTransaction transaccion= em.getTransaction();
		transaccion.begin();

		Curso c= new Curso("Java","Programacion",20,1);
		em.persist(c);
		transaccion.commit();
		em.close();	


	}

}

El programa nos insertará el Curso en la base de datos.  Podemos modificar el objeto y seleccionar un curso de Java con diferente nivel:

Curso c= new Curso(“Java”,”Programacion”,20,2);

El código se ejecutará sin ningún tipo de problema ya tenemos dos cursos en la base de datos:

Ahora bien si volvemos a intentar insertar el curso de nivel 1  nos saltará una excepción de clave duplicada.

Acabamos de crear un ejemplo con JPA composite key utilizando como clave compuesta el título y el nivel.

Otros artículos relacionados:

  1. Un ejemplo de JPA Entity Graph
  2. Ejemplo de JPA , Introducción (I)
  3. JPA Single Table Inheritance
It's only fair to share...Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

About Cecilio Álvarez Caules

Cecilio Álvarez Caules Sun Certified Enterprise Architech (J2EE/JEE).

8 Responses to JPA Composite Key y business objects

  1. José Viera 2 Mayo, 2017 at 19:02 #

    Mala practica.

    El uso de campos compuestos para crear llaves primarias, para mi es una mala practica. Introduce complejidad al código, existen formas mas simples de resolver el problema. Si no podemos elegir un solo campo en concreto para que se convierta en nuestra llave primaria, hay que optar por usar secuencias.
    Si necesitamos restringir la combinación de varios campos para que sean únicos, podemos usar constraint.

    Este comentario no tiene el propósito de criticar o de-meritar el Post de Cecilio, simplemente es mi punto particular de vista.

    • Cecilio Álvarez Caules 2 Mayo, 2017 at 22:24 #

      Siempre es interesante escucha otras opiniones Jose , te lo agradezco. Me podrías poner un enlace a un ejemplo concreto de una mejor opción .

      Muchas Gracias 🙂

      • José Viera 3 Mayo, 2017 at 0:23 #

        No tengo enlace para reforzar mi comentario, simplemente creo que usar mas de un campo para crear una llave primaria le agrega complejidad al código. Si por algún motivo, no existe un único campo que por si solo pueda ser usado como llave primaria, recomiendo (no es una regla, solo es una opinión personal) usar una secuencia o un campo autoincrementable.

        Saludos!

        • Cecilio Álvarez Caules 3 Mayo, 2017 at 8:20 #

          Yo si lo he podido echar un vistazo , porque me ha parecido una pregunta interesante ,Jose 🙂 . La conclusión que he sacado es que “depende” una composite key puede ser muy práctica ya que se generará un indice automáticamente para realizar las búsquedas y aunque el modelo se complique si usas delegación como en mi caso el desarrollador casi ni se entera. Sin embargo si esa composite key afecta a muchas tablas, es decir es FK en muchas suele ser más práctico definir una clave primaria auto incremental. Aquí te dejo un link a Tom kyte el experto de oracle que comenta el tema. .Espero que te ayude 🙂

          • Maria Ramos 6 Mayo, 2017 at 21:08 #

            Excelente intervención de ambos. Aclara escenarios

          • Cecilio Álvarez Caules 9 Mayo, 2017 at 18:45 #

            muchas gracias

          • Cornelio 7 Mayo, 2017 at 4:01 #

            Entonces, en un ejemplo más realista la clave primaria compuesta se compondría de referencias a otras tablas y un correlativo?
            Ejemplo:
            Para el caso que muestra esta reseña el título se referiria a otra tabla, sería una entidad distinta (Titulo) marcada con una relación @OneToMany.

            Quedaría así:
            //omitidos package e imports
            @Embeddable
            public class CursoPK implements Serializable {
            @OneToMany
            @JoinColumn(name=”tituloId”)
            private Titulo titulo;
            private int nivel;
            //omitimos getters setters y demás
            }

          • Cornelio 7 Mayo, 2017 at 4:13 #

            Correccion: quise decir @ManyToOne (dislexia)

Deja un comentario