In this Hibernate tutorial series, we are going to show you how to implement a bidirectional one-to-one association using JPA and Hibernate annotations. The annotations approach is preferred as an alternative to XML descriptor which is described in the tutorial Hibernate One-to-One With Primary Key XML Mapping Example. Let’s recall about the bidirectional one-to-one association by looking at the following entity relationship diagram:

 

one-to-one with primary key entity relationship diagram

As we can see, these two tables share a same primary key (product_id) so that it’s possible to navigate to a corresponding product_detail from a product and vice-versa, thus this is called bidirectional (two-ways) association on primary key.

In this tutorial, we will be developing a sample Hibernate program to manage the above product - product_detail association using the following pieces of software:

The software versions here are the latest as of this writing, but using similar versions is also possible. Here are the steps we should follow:

Table of content:

  1. Creating Database and Tables
  2. Creating Maven-Eclipse Project
  3. Coding Model Classes with Annotations
  4. Writing Hibernate Configuration File
  5. Writing a Test Program

 

1. Creating Database and Tables

Let’s create a database called productsdb containing two tables product and product_detail by executing the following MySQL script:

create database productsdb;
use productsdb;

CREATE TABLE `product` (
  `product_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `description` varchar(512) NOT NULL,
  `price` float NOT NULL,
  PRIMARY KEY (`product_id`)
);

CREATE TABLE `product_detail` (
  `product_id` int(11) NOT NULL,
  `part_number` varchar(45) NOT NULL,
  `dimension` varchar(45) NOT NULL,
  `weight` float NOT NULL,
  `manufacturer` varchar(45) NOT NULL,
  `origin` varchar(45) NOT NULL,
  PRIMARY KEY (`product_id`)
);

 We would have the following database structure:

productsdb database structure


2. Creating Maven-Eclipse Project

Eclipse Kepler has Maven integration by default, so creating a Maven project is quick and easy. Let’s create a Maven project with the following structure:

Hibernate one-to-one primary key annotations project structure

The project HibernateOne2OnePrimaryKeyAnnotationsExample consists of the following files:

  • Model classes: Product.java and ProductDetail.java
  • Hibernate XML configuration file: hibernate.cfg.xml
  • Test program: ProductsManager.java
  • Maven project: pom.xml

We specify two primary dependencies of hibernate-core and mysql-connector-java in the pom.xml file as follows:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>net.codejava.hibernate</groupId>
  <artifactId>HibernateOne2OnePrimaryKeyAnnotationsExample</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  	<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-core</artifactId>
  		<version>4.2.7.SP1</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.1.26</version>
  	</dependency>
  </dependencies>  
</project>

The other related dependencies will be resolved automatically by Maven.

 


3. Coding Model Classes with Annotations

Create two model classes called Product.java and ProductDetail.java with the following source code:

File net\codejava\hibernate\Product.java:

package net.codejava.hibernate;

import javax.persistence.CascadeType;
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;

@Entity
@Table(name = "PRODUCT")
public class Product {
	private long productId;
	private String name;
	private String description;
	private float price;
	private ProductDetail productDetail;

	public Product() {
	}

	@Id
	@GeneratedValue
	@Column(name = "PRODUCT_ID")
	public long getProductId() {
		return productId;
	}

	@OneToOne(cascade = CascadeType.ALL)
	@PrimaryKeyJoinColumn
	public ProductDetail getProductDetail() {
		return productDetail;
	}

	// other getters and setters
}

 

 

 File net\codejava\hibernate\ProductDetail.java:

package net.codejava.hibernate;

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

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

@Entity
@Table(name = "PRODUCT_DETAIL")
public class ProductDetail {
	private long productId;
	private String partNumber;
	private String dimension;
	private float weight;
	private String manufacturer;
	private String origin;
	private Product product;

	public ProductDetail() {
	}

	@Id
	@GeneratedValue(generator = "foreigngen")
	@GenericGenerator(strategy = "foreign", name="foreigngen",
			parameters = @Parameter(name = "property", value="product"))
	@Column(name = "PRODUCT_ID")
	public long getProductId() {
		return productId;
	}


	@Column(name = "PART_NUMBER")
	public String getPartNumber() {
		return partNumber;
	}

	@OneToOne(mappedBy = "productDetail")
	public Product getProduct() {
		return product;
	}

	// other getters and setters
}

Here, we use several annotations as you notice: The @Entity, @Table and @Column annotations are straightforward to understand. The others need some further explanations:

  • @Id and @GeneratedValue: are used in conjunction to map a field as the primary key of the table. Typically, the primary key’s values are auto-generated.
  • On the Product side, we use the @OneToOne and @PrimaryKeyJoinColumn annotations to tell Hibernate creates a one-to-one association with the ProductDetail and the join column is the primary key column.
  • On the ProductDetail side, we need to use the @GenericGenerator to specify a foreign key strategy in order to generate values for the product_id column as a foreign key. And the @OneToOne annotation tells Hibernate that the product field is a one-to-one association which is mapped by this productDetail.


4. Writing Hibernate Configuration File

We need to write XML descriptor in the hibernate.cfg.xml file to tell Hibernate which database to connect:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>        
  <session-factory>
    <!-- Database connection settings -->
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="connection.url">jdbc:mysql://localhost:3306/productsdb</property>
    <property name="connection.username">root</property>
    <property name="connection.password">secret</property>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    
    <mapping class="net.codejava.hibernate.Product"/>
    <mapping class="net.codejava.hibernate.ProductDetail"/>
      
  </session-factory>
</hibernate-configuration>

 

 

5. Writing a Test Program

Write code for the test program (ProductsManager.java) as follows:

package net.codejava.hibernate;

import java.util.List;

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

/**
 * This program demonstrates using Hibernate framework to manage a
 * bidirectional one-to-one association on a primary key using 
 * annotations.
 * @author www.codejava.net
 *
 */
public class ProductsManager {

	public static void main(String[] args) {
		// loads configuration and mappings
		Configuration configuration = new Configuration().configure();
		ServiceRegistryBuilder registry = new ServiceRegistryBuilder();
		registry.applySettings(configuration.getProperties());
		ServiceRegistry serviceRegistry = registry.buildServiceRegistry();
		
		// builds a session factory from the service registry
		SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
		
		// obtains the session
		Session session = sessionFactory.openSession();
		session.beginTransaction();
		
		// creates a new product
		Product product = new Product();
		product.setName("Civic");
		product.setDescription("Comfortable, fuel-saving car");
		product.setPrice(20000);
		
		// creates product detail
		ProductDetail detail = new ProductDetail();
		detail.setPartNumber("ABCDEFGHIJKL");
		detail.setDimension("2,5m x 1,4m x 1,2m");
		detail.setWeight(1000);
		detail.setManufacturer("Honda Automobile");
		detail.setOrigin("Japan");
		
		// sets the bi-directional association
		product.setProductDetail(detail);
		detail.setProduct(product);
		
		// persists the product
		session.save(product);
		
		// queries all products
		List<Product> listProducts = session.createQuery("from Product").list();
		for (Product aProd : listProducts) {
			String info = "Product: " + aProd.getName() + "\n";
			info += "\tDescription: " + aProd.getDescription() + "\n";
			info += "\tPrice: $" + aProd.getPrice() + "\n";
			
			ProductDetail aDetail = aProd.getProductDetail();
			info += "\tPart number: " + aDetail.getPartNumber() + "\n";
			info += "\tDimension: " + aDetail.getDimension() + "\n";
			info += "\tWeight: " + aDetail.getWeight() + "\n";
			info += "\tManufacturer: " + aDetail.getManufacturer() + "\n";
			info += "\tOrigin: " + aDetail.getOrigin() + "\n";
			
			System.out.println(info);
		}
		
		session.getTransaction().commit();
		session.close();
	}

}

Output of the program:

Hibernate: insert into PRODUCT (description, name, price) values (?, ?, ?)
Hibernate: insert into PRODUCT_DETAIL (dimension,...) values (?, ?, ?, ?, ?, ?)
Hibernate: select product0_.PRODUCT_ID ... from PRODUCT product0_
Product: Civic
	Description: Comfortable, fuel-saving car
	Price: $20000.0
	Part number: ABCDEFGHIJKL
	Dimension: 2,5m x 1,4m x 1,2m
	Weight: 1000.0
	Manufacturer: Honda Automobile
	Origin: Japan

Result in the product table:

records in product table

Result in the product_detail table:

records in product detail table

 

Recommended Book: Java Persistence with Hibernate

Submit to DeliciousSubmit to DiggSubmit to FacebookSubmit to Google BookmarksSubmit to StumbleuponSubmit to TechnoratiSubmit to TwitterSubmit to LinkedIn
Attachments:
Download this file (HibernateOne2OnePrimaryKeyAnnotationsExample.zip)HibernateOne2OnePrimaryKeyAnnotationsExample.zip[Eclipse-Maven project]17 kB