Hibernate con relaciones 1 a 1 unidireccionales

hibernateEn un artículo anterior vimos como configurar Hibernate para poder realizar la persistencia de nuestros objetos Java en una base de datos Oracle y MySQL. En esa ocasión trabajábamos únicamente con una sola tabla. En el presente artículo, aprenderemos a realizar la persistencia con dos tablas que poseen una relación 1 a 1 utilizando MySQL.

Si no has leído el artículo introductorio de Hibernate, te recomiendo que antes de seguir le eches un vistazo este post. En este artículo solo se van a nombrar las diferencias con el uso de hibernate en tablas simples (sin relaciones).

En este ejemplo, vamos a crear una sencilla aplicación para guardar en nuestra base de datos personas y sus direcciones correspondientes. En las relaciones con hibernate siempre tendremos una parte que será consciente de la relación y otra que no lo será. Que parte será una y que parte será la otra nos corresponderá a nosotros decidirlo durante el proceso de diseño.

En este caso, será persona la que sabrá que tiene una relación con dirección. Dirección no será consciente de que está relacionada con otra tabla.

Archivos de Mapeo

Dirección

Dirección no sabe que existe una relación con persona, con lo cual, su archivo de mapeo no contiene información adicional, únicamente contiene los atributos que esta posee.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.hibernate.pt2.data.model.Direccion" table="direcciones">
        <id name="id" column="iddireccion">
            <generator class="sequence">
                   <param name="sequence">direcciones_SQ_Hibernate</param>
            </generator>
        </id>
        <property name="calle">
            <column name="calle" length="30" not-null="true" />
        </property>
        <property name="cp">
            <column name="cp" length="8" not-null="true" />
        </property>       
    </class>

</hibernate-mapping>

Persona

Persona sí que es consciente de que mantiene una relación con dirección con lo cual, podemos ver que tiene cierta información sobre ella.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.hibernate.pt2.data.model.Persona" table="personas">
        <!-- 
            ID de persona y dirección será el mismo.
            En relaciones 1 a 1 hibernate supone que eso es así.
            Como dirección no sabe que existe persona pero persona si sabe que existe
            direccion, es persona la que "copiará" el id de dirección.
            foreign indica que el id será tomado de la otra parte de la relación. 
            Esa entidad se referencia con
            la propiedad direccion (ver clase de modelo)
         -->
        <id name="id" column="idpersona">
            <generator class="foreign"> 
                <param name="property">direccion</param> 
            </generator>
        </id>
        <property name="nombre">
            <column name="nombre" length="15" not-null="true" />
        </property>
        <property name="apellidos">
            <column name="apellidos" length="30" not-null="true" />
        </property>    

        <!-- 
            Indicamos que la relación es uno a uno. 
            Con name indicamos como se llama el atributo de la clase persona
            que indica la relación con dirección, en este caso, direccion.
            Queremos que una dirección se cree al crear una persona 
            y se elimine al borrar la persona. Por eso
            añadimos el atributo cascade con persist y delete.
         -->
        <one-to-one name="direccion" cascade="persist, delete"/>
        <!-- 
            Otros valores de cascade:
                persist
                merge
                save-update
                delete
                lock
                refresh
                evict
                replicate
                all
                none
         -->
    </class>

</hibernate-mapping>

Archivos de Modelo

Los archivos de modelo para estas clases serán los siguientes.

Dirección

Recordemos que dirección desconoce que existe cualquier tipo de relación, con lo cual, tendremos una clase de modelo sin ninguna diferencia a las estudiadas en el artículo anterior.

package com.hibernate.pt2.data.model;

public class Direccion {

	private Integer id;
	private String calle;
	private String cp;

	private void setId(Integer id){
		this.id=id;
	}

	public void setCp(String cp){
		this.cp=cp;
	}

	public void setCalle(String calle){
		this.calle=calle;
	}

	public Integer getId(){
		return id;
	}

	public String getCalle(){
		return calle;
	}

	public String getCp(){
		return cp;
	}
}

Persona

Persona si que es consciente de que existe una relación con dirección. Podemos observar como será la propia persona la que «guardará» su dirección.

package com.hibernate.pt2.data.model;

public class Persona {

	private Integer id;
	private String nombre;
	private String apellidos;

	private Direccion direccion;

	public void setDireccion(Direccion direccion){
		this.direccion = direccion;
	}

	public Direccion getDireccion(){
		return direccion;
	}

	private void setId(Integer id){
		this.id = id;
	}

	public void setNombre(String nombre){
		this.nombre=nombre;
	}

	public void setApellidos(String apellidos){
		this.apellidos = apellidos;
	}

	public Integer getId(){
		return id;
	}

	public String getNombre(){
		return nombre;
	}

	public String getApellidos(){
		return apellidos;
	}
}

Configuración de Hibernate

La configuración de hibernate para relaciones deberá contener todos los archivos de mapeo utilizados en ella.

<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>

        <!-- Dialecto Hibernate -->
        <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>

        <!-- Conexión a la base de datos -->
        <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
        <property name="hibernate.connection.url">jdbc:Oracle:thin:@192.168.100.100:1521:XE</property>
        <property name="hibernate.connection.username">aitor</property>
        <property name="hibernate.connection.password">P@ssw0rd</property>
        <property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>

        <!-- Muestra el código SQL generado por hibernate sobre a base de datos -->
        <property name="show_sql">true</property>

        <!-- Creación de esquema automática update-->
        <property name="hibernate.hbm2ddl.auto">update</property>  

        <!-- Configurar la cache de hibernate -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>

        <!-- Se unirá la sesión al thread en el que se ha llamado -->
        <property name="current_session_context_class">thread</property>

        <!-- Indicamos los ficheros de Mapeo -->
        <mapping resource="com/hibernate/pt2/data/mapping/Persona.hbm.xml" />
        <mapping resource="com/hibernate/pt2/data/mapping/Direccion.hbm.xml" />

    </session-factory>
</hibernate-configuration>

Clase HibernateUtils

Para facilitar el trabajo con Hibernate, podemos crear alguna clase para el manejo de las sesiones y la interacción con la base de datos. Esta clase es solo un ejemplo con lo que podéis reemplazarla si lo consideráis oportuno.

package com.hibernate.pt2;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtils {

	private static final SessionFactory sessionFactory;

	static {
        try {       	
        	Configuration configuration = new Configuration().configure();

        	ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties())
        	              .buildServiceRegistry();
        	sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        } catch (Throwable ex) {
            System.err.println("Excepcion en HibernateUtils. Creación de SessionFactory." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

	public static Session openSession(){
		Session session = HibernateUtils.getSessionFactory().openSession();
		session.beginTransaction();

		return session;
	}

	public static void closeSession(Session s){
		//s.getTransaction().commit();
		s.close();
	}
}

Cliente

Esta clase será la que usaremos para trabajar con las personas y las direcciones. Aquí crearemos a diferentes personas y les asignaremos sus direcciones correspondientes.

package com.hibernate.pt2;

import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;

import com.hibernate.pt2.data.model.Direccion;
import com.hibernate.pt2.data.model.Persona;

public class Practica2 {

	public static void main(String[] args) {

		Persona p1 = new Persona();
		Direccion d1 = new Direccion();
		Persona p2 = new Persona();
		Direccion d2 = new Direccion();
		Direccion d3 = new Direccion();

		p1.setNombre("Aitor");
		p1.setApellidos("Díaz");
		p1.setDireccion(d1);

		d1.setCalle("Mimoses S/N");
		d1.setCp("08080");

		p2.setNombre("Jose");
		p2.setApellidos("Perez");
		p2.setDireccion(d2);

		d2.setCalle("Barcelona 32, 1º");
		d2.setCp("99999");

		d3.setCalle("Jacint Verdaguer 45");
		d3.setCp("00000");

		Session s = HibernateUtils.openSession();
		/*
		 * Atención!! Intentemos adivinar que debería pasar con los identificadores
                 * de estos objetos al guardarse en la BD.
		 * Que ids tendrán direccion y persona?
		 * dir1 id=1
		 * per1 id=1
		 * dir3 id=2
		 * dir2 id=3
		 * per2 id=2
		 * 
		 * Es correcto? Los id normalmente son correlativos en cada tabla,
                 * también será así en este caso?
		 * Ver Persona.hbm.xml para aclaración!
		 */
		s.persist(p1);
		s.persist(d3);
		s.persist(p2);

		s.getTransaction().commit();
		HibernateUtils.closeSession(s); //s.close();

		//Eliminación de registros

		//Session s2 = HibernateUtils.openSession();
		//s2.delete(p1);
		//s2.getTransaction().commit();
		//s2.close();

		//Buscamos y obtenemos Personas y Direcciones de la BD
		Session s3 = HibernateUtils.openSession();
		List li = getPersonaPorNombre(s3, "Aitor");
		s3.close();

		Persona p = li.get(0);
		System.out.println("Apellidos: "+p.getApellidos());
		System.out.println("Direccion: "+p.getDireccion().getCalle());

	}

	public static List getPersonaPorNombre(Session s, String name) throws HibernateException { 
        List listaEntradas = null;  

        try { 
            listaEntradas = s.createQuery("from Persona where nombre='"+name+"'").list(); 
        } finally {}  

        return listaEntradas; 
    }
}

Podemos utilizar estos sencillos ejemplos como base para aprender a trabajar con hibernate en relaciones 1 a 1.

One Response to Hibernate con relaciones 1 a 1 unidireccionales

  1. Pingback: Hibernate con relaciones 1 a N unidireccionales | Aitor Rigada

Deja un comentario