JPA OneToMany Unidirectional with Join Table

Posted on

In earlier post we had OneToMany bi-directional mapping. In this post we will see how can we make it unidirectional with the usage of a JoinTable.
Let’s create our eclipse project and then add update our pom.xml.

pom.xml

<project
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://maven.apache.org/POM/4.0.0"
	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>com.mumz.test.hibernatesearch</groupId>
	<artifactId>MumzHibernateSearch</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>MumzHibernateSearch</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>1.0.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.21</version>
		</dependency>
	</dependencies>
</project>

Next we will write our Entity to model Book and BookShelf tables. BookShelf will contain a set of books. First we will write our MHSBookEntityBean.

MHSBookEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import javax.persistence.*;

/**
 * The Class MHSBookEntityBean.
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK")
public class MHSBookEntityBean implements Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long	serialVersionUID	= -5129783468137830152L;
	
	/** The id. */
	private Long				id					= null;
	
	/** The name. */
	private String				name				= null;
	
	/** The author. */
	private String				author				= null;
	
	/**
	 * Instantiates a new mHS book entity bean.
	 */
	public MHSBookEntityBean() {
		super();
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_ID")
	public Long getId() {
		return this.id;
	}
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_NAME")
	public String getName() {
		return this.name;
	}
	
	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Gets the author.
	 * 
	 * @return the author
	 */
	@Column(name = "BOOK_AUTHOR")
	public String getAuthor() {
		return author;
	}
	
	/**
	 * Sets the author.
	 * 
	 * @param author
	 *            the new author
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookEntityBean)) {
			return false;
		}
		MHSBookEntityBean other = (MHSBookEntityBean) obj;
		if (author == null) {
			if (other.author != null) {
				return false;
			}
		} else if (!author.equals(other.author)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookEntityBean [id=" + id + ", name=" + name + ", author=" + author + "]";
	}
}

Next we will write our MHSBookShelfEntityBean

MHSBookShelfEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

/**
 * The Class MHSBookShelfEntityBean.
 * 
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK_SHELF")
public class MHSBookShelfEntityBean implements Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long		serialVersionUID	= -7127365575633206221L;
	
	/** The id. */
	private Long					id;
	
	/** The name. */
	private String					name;
	
	/** The books. */
	private Set<MHSBookEntityBean>	books				= new HashSet<MHSBookEntityBean>();
	
	/**
	 * Instantiates a new mHS book shelf entity bean.
	 */
	public MHSBookShelfEntityBean() {
		super();
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_SHELF_ID")
	public Long getId() {
		return this.id;
	}
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_SHELF_NAME")
	public String getName() {
		return this.name;
	}
	
	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Gets the books.
	 * 
	 * @return the books
	 */
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinTable(name = "BOOK_SHELF_BOOK_MAPPING", joinColumns = {
		@JoinColumn(name = "BOOK_SHELF_ID", referencedColumnName = "BOOK_SHELF_ID")
	}, inverseJoinColumns = {
		@JoinColumn(name = "BOOK_ID", referencedColumnName = "BOOK_ID", unique = true)
	})
	public Set<MHSBookEntityBean> getBooks() {
		return books;
	}
	
	/**
	 * Sets the books.
	 * 
	 * @param books
	 *            the new books
	 */
	public void setBooks(Set<MHSBookEntityBean> books) {
		this.books = books;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((books == null) ? 0 : books.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookShelfEntityBean)) {
			return false;
		}
		MHSBookShelfEntityBean other = (MHSBookShelfEntityBean) obj;
		if (books == null) {
			if (other.books != null) {
				return false;
			}
		} else if (!books.equals(other.books)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookShelfEntityBean [id=" + id + ", name=" + name + ", books=" + books + "]";
	}
}

Highlighted code above signifies the Join table usage.

Finally our persistence.xml and some test code to check our model.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" >
    <persistence-unit name="MumzJPA" transaction-type="RESOURCE_LOCAL">
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookEntityBean</class>
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookShelfEntityBean</class>
        <properties>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost/jpa_schema"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

TestOneToManyJoinTable.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.Persistence;

/**
 * The Class TestOneToManyJoinTable.
 * @author prabhat.jha
 */
public class TestOneToManyJoinTable {
	
	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 */
	public static void main(String[] args) {
		EntityManager entityManager = Persistence.createEntityManagerFactory("MumzJPA").createEntityManager();
		try {
			Set<MHSBookEntityBean> books = new HashSet<MHSBookEntityBean>();
			MHSBookEntityBean mhsBookEntityBean = new MHSBookEntityBean();
			mhsBookEntityBean.setName("Java EE Pocket Guide");
			mhsBookEntityBean.setAuthor("Arun Gupta");
			books.add(mhsBookEntityBean);
			mhsBookEntityBean = new MHSBookEntityBean();
			mhsBookEntityBean.setName("Pro Android 4");
			mhsBookEntityBean.setAuthor("Satya Komatineni and Dave MacLean");
			books.add(mhsBookEntityBean);
			
			MHSBookShelfEntityBean bookShelfEntityBean = new MHSBookShelfEntityBean();
			bookShelfEntityBean.setName("Technical Books");
			bookShelfEntityBean.setBooks(books);
			
			entityManager.getTransaction().begin();
			entityManager.persist(bookShelfEntityBean);
			entityManager.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			entityManager.getTransaction().rollback();
		} finally {
			if (entityManager != null) {
				entityManager.close();
			}
			entityManager = null;
		}
	}
	
}

Database schema used for this code.

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

DROP SCHEMA IF EXISTS `jpa_schema` ;
CREATE SCHEMA IF NOT EXISTS `jpa_schema` DEFAULT CHARACTER SET utf8 ;
USE `jpa_schema` ;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book` (
  `BOOK_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_AUTHOR` VARCHAR(45) NOT NULL ,
  `BOOK_NAME` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`BOOK_ID`) )
ENGINE = InnoDB
AUTO_INCREMENT = 5
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book_shelf`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book_shelf` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book_shelf` (
  `BOOK_SHELF_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_SHELF_NAME` VARCHAR(45) NULL DEFAULT NULL ,
  PRIMARY KEY (`BOOK_SHELF_ID`) )
ENGINE = InnoDB
AUTO_INCREMENT = 3
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book_shelf_book_mapping`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book_shelf_book_mapping` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book_shelf_book_mapping` (
  `BOOK_SHELF_BOOK_MAP_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_ID` INT(11) NULL DEFAULT NULL ,
  `BOOK_SHELF_ID` INT(11) NULL DEFAULT NULL ,
  PRIMARY KEY (`BOOK_SHELF_BOOK_MAP_ID`) ,
  INDEX `FK_BOOK_ID` (`BOOK_ID` ASC) ,
  INDEX `FK_BOOK_SHELF_ID` (`BOOK_SHELF_ID` ASC) ,
  CONSTRAINT `FK_BOOK_ID`
    FOREIGN KEY (`BOOK_ID` )
    REFERENCES `jpa_schema`.`book` (`BOOK_ID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `FK_BOOK_SHELF_ID`
    FOREIGN KEY (`BOOK_SHELF_ID` )
    REFERENCES `jpa_schema`.`book_shelf` (`BOOK_SHELF_ID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
AUTO_INCREMENT = 5
DEFAULT CHARACTER SET = utf8;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

One thought on “JPA OneToMany Unidirectional with Join Table

    Tapan Gupta said:
    January 15, 2015 at 3:14 AM

    I have a question here. I have company and it’s subscription as two tables. Subscription for me is a reference table which has fix data. So I am creating a new company and want to store it’s subscription in Join table. How can I achieve this. When I try above thing it’s trying to insert in subscription table which I don’t want.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s