Ejemplo de Mapeo Uno a Uno de Hibernate con Anotaciones

Hoy vamos a analizar el Mapeo Uno a Uno en Hibernate. Veremos un ejemplo de Mapeo Uno a Uno en Hibernate utilizando anotaciones y configuración XML.

Mapeo Uno a Uno en Hibernate

La mayoría de las veces, las tablas de la base de datos están asociadas entre sí. Hay muchas formas de asociación: uno a uno, uno a muchos y muchos a muchos están en un nivel amplio. Estos pueden dividirse además en mapeos unidireccionales y bidireccionales. Hoy veremos cómo implementar el Mapeo Uno a Uno en Hibernate utilizando configuración XML así como utilizando configuración con anotaciones.

Configuración de Base de Datos del Ejemplo de Mapeo Uno a Uno en Hibernate

Primero que nada, necesitaríamos configurar una asignación uno a uno en las tablas de la base de datos. Crearemos dos tablas para nuestro ejemplo: Transacción y Cliente. Ambas tablas tendrán una asignación uno a uno. La Transacción será la tabla principal y utilizaremos una Clave Foránea en la tabla Cliente para la asignación uno a uno. A continuación, proporciono el script MySQL, que es la base de datos que estoy utilizando para este tutorial. Si estás utilizando alguna otra base de datos, asegúrate de cambiar el script en consecuencia.

-- Crear Tabla de Transacción
CREATE TABLE `Transaction` (
  `txn_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `txn_date` date NOT NULL,
  `txn_total` decimal(10,0) NOT NULL,
  PRIMARY KEY (`txn_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;

-- Crear tabla de Cliente
CREATE TABLE `Customer` (
  `txn_id` int(11) unsigned NOT NULL,
  `cust_name` varchar(20) NOT NULL DEFAULT '',
  `cust_email` varchar(20) DEFAULT NULL,
  `cust_address` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`txn_id`),
  CONSTRAINT `customer_ibfk_1` FOREIGN KEY (`txn_id`) REFERENCES `Transaction` (`txn_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

El Diagrama de Relación de Entidades (ERD) de la asignación uno a uno entre las tablas se ve como la siguiente imagen. Nuestra configuración de base de datos está lista, ahora pasemos al Proyecto de Ejemplo de Hibernate Uno a Uno.

Estructura del Proyecto de Ejemplo de Hibernate Uno a Uno

Cree un proyecto Maven simple en su IDE de Java, estoy usando Eclipse. Nuestra estructura de proyecto final se verá como en la imagen a continuación. Primero, veremos un ejemplo de mapeo uno a uno de Hibernate basado en XML y luego implementaremos lo mismo usando anotaciones.

Dependencias de Maven para Hibernate

Nuestro archivo pom.xml final se verá así.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.journaldev.hibernate</groupId>
  <artifactId>HibernateOneToOneMapping</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  	<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-core</artifactId>
  		<version>4.3.5.Final</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.0.5</version>
  	</dependency>
  </dependencies>
</project>

Las dependencias son solo para Hibernate y el controlador de Java MySQL. Tenga en cuenta que estoy usando la última versión de Hibernate 4.3.5.Final y el controlador de Java MySQL basado en la versión de mi servidor de base de datos MySQL (5.0.5). Hibernate 4 utiliza el registro de JBoss y se importa automáticamente como una dependencia transitiva. Puede confirmarlo en las dependencias de Maven del proyecto. Si está utilizando versiones anteriores de Hibernate, es posible que tenga que agregar dependencias de slf4j.

Clases de modelo de mapeo uno a uno de Hibernate

Las clases de modelo para el mapeo uno a uno de Hibernate para reflejar las tablas de la base de datos serían como las siguientes.

package com.journaldev.hibernate.model;

import java.util.Date;

public class Txn {

	private long id;
	private Date date;
	private double total;
	private Customer customer;
	
	@Override
	public String toString(){
		return id+", "+total+", "+customer.getName()+", "+customer.getEmail()+", "+customer.getAddress();
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public double getTotal() {
		return total;
	}
	public void setTotal(double total) {
		this.total = total;
	}
	public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	
}
package com.journaldev.hibernate.model;

public class Customer {

	private long id;
	private String name;
	private String email;
	private String address;
	
	private Txn txn;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public Txn getTxn() {
		return txn;
	}
	public void setTxn(Txn txn) {
		this.txn = txn;
	}
	
}

Dado que estamos utilizando una configuración basada en XML para el mapeo, las clases de modelo anteriores son simples clases POJO o Java Beans con métodos getter-setter. Estoy utilizando el nombre de clase como Txn para evitar confusiones porque la API de Hibernate tiene una clase con el nombre Transaction.

Configuración de Mapeo Uno a Uno de Hibernate

Vamos a crear archivos de configuración de mapeo uno a uno de Hibernate para las tablas Txn y Customer. txn.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.journaldev.hibernate.model.Txn" table="TRANSACTION" >
		<id name="id" type="long">
			<column name="txn_id" />
			<generator class="identity" />
		</id>
		<property name="date" type="date">
			<column name="txn_date" />
		</property>
		<property name="total" type="double">
			<column name="txn_total" />
		</property>
		<one-to-one name="customer" class="com.journaldev.hibernate.model.Customer"
			cascade="save-update" />
	</class>
	
</hibernate-mapping>

El punto importante a tener en cuenta es el elemento one-to-one de Hibernate para la propiedad de customer. customer.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>

	<class name="com.journaldev.hibernate.model.Customer" table="CUSTOMER">
		<id name="id" type="long">
			<column name="txn_id" />
			<generator class="foreign">
				<param name="property">txn</param>
			</generator>
		</id>
		<one-to-one name="txn" class="com.journaldev.hibernate.model.Txn"
			constrained="true"></one-to-one>

		<property name="name" type="string">
			<column name="cust_name"></column>
		</property>
		<property name="email" type="string">
			<column name="cust_email"></column>
		</property>
		<property name="address" type="string">
			<column name="cust_address"></column>
		</property>
	</class>

</hibernate-mapping>

La parte importante es la clase de generador “foreign” que se utiliza para la implementación de la clave foránea de Hibernate.

Archivo de Configuración de Hibernate

Aquí está el archivo de configuración de Hibernate para la configuración de mapeo de Hibernate basada en XML. hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pankaj123</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
        <property name="hibernate.connection.username">pankaj</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
        
        <mapping resource="txn.hbm.xml"/>
        <mapping resource="customer.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

El archivo de configuración de Hibernate es simple, contiene propiedades de conexión a la base de datos y recursos de mapeo de Hibernate.

Utilidad de la Fábrica de Sesiones de Hibernate

Aquí está la clase de utilidad para crear una instancia de la Fábrica de Sesiones de Hibernate.

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil {

	private static SessionFactory sessionFactory;
	
	private static SessionFactory buildSessionFactory() {
        try {
            // Crear la SessionFactory desde hibernate.cfg.xml
        	Configuration configuration = new Configuration();
        	configuration.configure("hibernate.cfg.xml");
        	System.out.println("Hibernate Configuration loaded");
        	
        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        	System.out.println("Hibernate serviceRegistry created");
        	
        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        	
            return sessionFactory;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }
	
	public static SessionFactory getSessionFactory() {
		if(sessionFactory == null) sessionFactory = buildSessionFactory();
        return sessionFactory;
    }
}

Eso es todo, vamos a escribir un programa de prueba para probar la configuración basada en XML del mapeo uno a uno de hibernate.

Programa de Prueba de Configuración de Mapeo Uno a Uno de Hibernate XML

En el ejemplo del programa de prueba de mapeo uno a uno de hibernate, primero crearemos un objeto Txn y lo guardaremos. Una vez que esté guardado en la base de datos, usaremos el ID generado para recuperar el objeto Txn e imprimirlo.

package com.journaldev.hibernate.main;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Customer;
import com.journaldev.hibernate.model.Txn;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateOneToOneMain {

	public static void main(String[] args) {
		
		Txn txn = buildDemoTransaction();
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//Obtener Sesión
		sessionFactory = HibernateUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created");
		//iniciar transacción
		tx = session.beginTransaction();
		//Guardar el objeto del Modelo
		session.save(txn);
		//Confirmar transacción
		tx.commit();
		System.out.println("Transaction ID="+txn.getId());
		
		//Obtener Datos de Transacción Guardados
		printTransactionData(txn.getId(), sessionFactory);
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

	private static void printTransactionData(long id, SessionFactory sessionFactory) {
		Session session = null;
		Transaction tx = null;
		try{
			//Obtener Sesión
			sessionFactory = HibernateUtil.getSessionFactory();
			session = sessionFactory.getCurrentSession();
			//iniciar transacción
			tx = session.beginTransaction();
			//Guardar el objeto del Modelo
			Txn txn = (Txn) session.get(Txn.class, id);
			//Confirmar transacción
			tx.commit();
			System.out.println("Transaction Details=\n"+txn);
			
			}catch(Exception e){
				System.out.println("Exception occured. "+e.getMessage());
				e.printStackTrace();
			}
	}

	private static Txn buildDemoTransaction() {
		Txn txn = new Txn();
		txn.setDate(new Date());
		txn.setTotal(100);
		
		Customer cust = new Customer();
		cust.setAddress("Bangalore, India");
		cust.setEmail("[email protected]");
		cust.setName("Pankaj Kumar");
		
		txn.setCustomer(cust);
		
		cust.setTxn(txn);
		return txn;
	}

}

Ahora, cuando ejecutamos el programa de prueba de mapeo uno a uno en hibernate, obtenemos la siguiente salida.

Hibernate Configuration loaded
Hibernate serviceRegistry created
Session created
Hibernate: insert into TRANSACTION (txn_date, txn_total) values (?, ?)
Hibernate: insert into CUSTOMER (cust_name, cust_email, cust_address, txn_id) values (?, ?, ?, ?)
Transaction ID=19
Hibernate: select txn0_.txn_id as txn_id1_1_0_, txn0_.txn_date as txn_date2_1_0_, txn0_.txn_total as txn_tota3_1_0_, 
customer1_.txn_id as txn_id1_0_1_, customer1_.cust_name as cust_nam2_0_1_, customer1_.cust_email as cust_ema3_0_1_, 
customer1_.cust_address as cust_add4_0_1_ from TRANSACTION txn0_ left outer join CUSTOMER customer1_ on 
txn0_.txn_id=customer1_.txn_id where txn0_.txn_id=?
Transaction Details=
19, 100.0, Pankaj Kumar, [email protected], Bangalore, India
Closing SessionFactory

Como puedes ver, todo está funcionando bien y podemos recuperar datos de ambas tablas utilizando el ID de transacción. Verifica el SQL utilizado internamente por Hibernate para obtener los datos; está utilizando joins para obtener información de ambas tablas.

Mapeo uno a uno en Hibernate con anotaciones

En la sección anterior, vimos cómo utilizar la configuración basada en XML para el mapeo uno a uno en Hibernate. Ahora veamos cómo podemos usar JPA y las anotaciones de Hibernate para lograr lo mismo.

Archivo de Configuración de Hibernate

hibernate-annotation.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pankaj123</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
        <property name="hibernate.connection.username">pankaj</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
        
        <mapping class="com.journaldev.hibernate.model.Txn1"/>
        <mapping class="com.journaldev.hibernate.model.Customer1"/>
    </session-factory>
</hibernate-configuration>

La configuración de Hibernate es sencilla, como puedes ver, tengo dos clases de modelo que usaremos con anotaciones: Txn1 y Customer1.

Ejemplo de clases de modelo con mapeo uno a uno en Hibernate usando anotaciones

Para la configuración de anotaciones de mapeo uno a uno de Hibernate, las clases de modelo son la parte más importante. Veamos cómo se ven nuestras clases de modelo.

package com.journaldev.hibernate.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;

@Entity
@Table(name="TRANSACTION")
public class Txn1 {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="txn_id")
	private long id;
	
	@Column(name="txn_date")
	private Date date;
	
	@Column(name="txn_total")
	private double total;
	
	@OneToOne(mappedBy="txn")
	@Cascade(value=org.hibernate.annotations.CascadeType.SAVE_UPDATE)
	private Customer1 customer;
	
	@Override
	public String toString(){
		return id+", "+total+", "+customer.getName()+", "+customer.getEmail()+", "+customer.getAddress();
	}

         // Métodos Getter-Setter, omitidos por claridad  
}

Observe que la mayoría de las anotaciones son de Java Persistence API porque Hibernate proporciona su implementación. Sin embargo, para el cascading, necesitaríamos usar la anotación de Hibernate org.hibernate.annotations.Cascade y el enum org.hibernate.annotations.CascadeType.

package com.journaldev.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
@Table(name="CUSTOMER")
public class Customer1 {

	@Id
	@Column(name="txn_id", unique=true, nullable=false)
	@GeneratedValue(generator="gen")
	@GenericGenerator(name="gen", strategy="foreign", parameters={@Parameter(name="property", value="txn")})
	private long id;
	
	@Column(name="cust_name")
	private String name;
	
	@Column(name="cust_email")
	private String email;
	
	@Column(name="cust_address")
	private String address;
	
	@OneToOne
	@PrimaryKeyJoinColumn
	private Txn1 txn;

         // Métodos Getter-Setter 
}

Tenga en cuenta que necesitaríamos @GenericGenerator para que el id se use del txn en lugar de generarlo.

Clase de utilidad Hibernate SessionFactory

La creación de SessionFactory es independiente de la forma en que proporcionamos el mapeo de Hibernate. Nuestra clase de utilidad para crear SessionFactory se ve así.

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateAnnotationUtil {

	private static SessionFactory sessionFactory;
	
	private static SessionFactory buildSessionFactory() {
        try {
             // Cree la SessionFactory desde hibernate-annotation.cfg.xml 
        	Configuration configuration = new Configuration();
        	configuration.configure("hibernate-annotation.cfg.xml");
        	System.out.println("Hibernate Annotation Configuration loaded");
        	
        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        	System.out.println("Hibernate Annotation serviceRegistry created");
        	
        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        	
            return sessionFactory;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }
	
	public static SessionFactory getSessionFactory() {
		if(sessionFactory == null) sessionFactory = buildSessionFactory();
        return sessionFactory;
    }
}

Ejemplo de programa de prueba de mapeo uno a uno de Hibernate con anotaciones

Aquí hay un programa de prueba simple para nuestro ejemplo de mapeo uno a uno de Hibernate con anotaciones.

package com.journaldev.hibernate.main;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Customer1;
import com.journaldev.hibernate.model.Txn1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;

public class HibernateOneToOneAnnotationMain {

	public static void main(String[] args) {
		
		Txn1 txn = buildDemoTransaction();
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//Obtener Sesión
		sessionFactory = HibernateAnnotationUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created using annotations configuration");
		//iniciar transacción
		tx = session.beginTransaction();
		//Guardar el objeto Modelo
		session.save(txn);
		//Confirmar transacción
		tx.commit();
		System.out.println("Annotation Example. Transaction ID="+txn.getId());
		
		//Obtener Datos de Transacción Guardados
		printTransactionData(txn.getId(), sessionFactory);
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

	private static void printTransactionData(long id, SessionFactory sessionFactory) {
		Session session = null;
		Transaction tx = null;
		try{
			//Obtener Sesión
			sessionFactory = HibernateAnnotationUtil.getSessionFactory();
			session = sessionFactory.getCurrentSession();
			//iniciar transacción
			tx = session.beginTransaction();
			//Guardar el objeto Modelo
			Txn1 txn = (Txn1) session.get(Txn1.class, id);
			//Confirmar transacción
			tx.commit();
			System.out.println("Annotation Example. Transaction Details=\n"+txn);
			
			}catch(Exception e){
				System.out.println("Exception occured. "+e.getMessage());
				e.printStackTrace();
			}
	}

	private static Txn1 buildDemoTransaction() {
		Txn1 txn = new Txn1();
		txn.setDate(new Date());
		txn.setTotal(100);
		
		Customer1 cust = new Customer1();
		cust.setAddress("San Jose, USA");
		cust.setEmail("[email protected]");
		cust.setName("Pankaj Kr");
		
		txn.setCustomer(cust);
		
		cust.setTxn(txn);
		return txn;
	}

}

Aquí está el fragmento de salida cuando ejecutamos el programa anterior.

Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Session created using annotations configuration
Hibernate: insert into TRANSACTION (txn_date, txn_total) values (?, ?)
Hibernate: insert into CUSTOMER (cust_address, cust_email, cust_name, txn_id) values (?, ?, ?, ?)
Annotation Example. Transaction ID=20
Hibernate: select txn1x0_.txn_id as txn_id1_1_0_, txn1x0_.txn_date as txn_date2_1_0_, txn1x0_.txn_total as txn_tota3_1_0_, 
customer1x1_.txn_id as txn_id1_0_1_, customer1x1_.cust_address as cust_add2_0_1_, customer1x1_.cust_email as cust_ema3_0_1_, 
customer1x1_.cust_name as cust_nam4_0_1_ from TRANSACTION txn1x0_ left outer join CUSTOMER customer1x1_ on 
txn1x0_.txn_id=customer1x1_.txn_id where txn1x0_.txn_id=?
Annotation Example. Transaction Details=
20, 100.0, Pankaj Kr, [email protected], San Jose, USA
Closing SessionFactory

Observa que la salida es similar a la configuración XML basada en hibernate one to one. Eso es todo para el ejemplo de mapeo uno a uno de Hibernate, puedes descargar el proyecto final desde el siguiente enlace y jugar con él para aprender más.

Descargar Proyecto de Mapeo Hibernate OneToOne

Source:
https://www.digitalocean.com/community/tutorials/hibernate-one-to-one-mapping-example-annotation