CREATE DATABASE `newsdb`; use newsdb; CREATE TABLE `category` ( `category_id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL, PRIMARY KEY (`category_id`) ); CREATE TABLE `article` ( `article_id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(70) NOT NULL, `description` varchar(250) NOT NULL, `keywords` varchar(150) NOT NULL, `content` text NOT NULL, PRIMARY KEY (`article_id`) ); CREATE TABLE `category_article` ( `category_id` int(11) NOT NULL, `article_id` int(11) NOT NULL, PRIMARY KEY (`category_id`,`article_id`), UNIQUE KEY `article_id_UNIQUE` (`article_id`), KEY `fk_category` (`category_id`), KEY `fk_article` (`article_id`), CONSTRAINT `fk_article` FOREIGN KEY (`article_id`) REFERENCES `article` (`article_id`), CONSTRAINT `fk_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`category_id`) );Notice that the column article_id of the join table is set with unique key constraint to enforce the one-to-many association, meaning that there is no one article belongs to more than one category.
<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>HibernateOne2ManyJoinTableAnnotationsExample</artifactId> <version>1.0</version> <description>A sample Hibernate application that maps a one-to-many association on join table using annotations</description> <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>All other dependencies will be resolved automatically by Maven.
package net.codejava.hibernate; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name = "CATEGORY") public class Category { private long id; private String name; private Set<Article> articles; public Category() { } public Category(String name) { this.name = name; } @Id @GeneratedValue @Column(name = "CATEGORY_ID") public long getId() { return id; } @OneToMany(cascade = CascadeType.ALL) @JoinTable( name = "CATEGORY_ARTICLE", joinColumns = @JoinColumn(name = "CATEGORY_ID"), inverseJoinColumns = @JoinColumn(name = "ARTICLE_ID") ) // other getters and setters... }File net\codejava\hibernate\Article.java:
package net.codejava.hibernate; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "ARTICLE") public class Article { private long id; private String title; private String description; private String keywords; private String content; public Article() { } public Article(String title, String description, String keywords, String content) { this.title = title; this.description = description; this.keywords = keywords; this.content = content; } @Id @GeneratedValue @Column(name = "ARTICLE_ID") public long getId() { return id; } // other getters and setters... }We can see that a Category has a set of Articles, but an Article doesn’t have any back reference to the Category, thus this association is unidirectional.To map the one-to-many association on join table, these JPA annotations are used: @OneToMany, @JoinTable and @JoinColumn, besides the basic annotations (@Entity, @Column, @Id, etc) . Let’s look at the code in the Category side closely:
private Set<Article> articles; @OneToMany(cascade = CascadeType.ALL) @JoinTable( name = "CATEGORY_ARTICLE", joinColumns = @JoinColumn(name = "CATEGORY_ID"), inverseJoinColumns = @JoinColumn(name = "ARTICLE_ID") ) public Set<Article> getArticles() { return articles; }
Here, we use the @JoinTable annotation to specify the details of the join table (table name and two join columns - using the @JoinColumn annotation); and we set the cascade attribute of the @OneToMany annotation so that Hibernate will update the associated articles when the category is updated.
And note that we don’t have to use any special annotations for the Article side, as this association is unidirectional; and there is no need to create a mapping class for the join table.<?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/newsdb</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.Category"/> <mapping class="net.codejava.hibernate.Article"/> </session-factory> </hibernate-configuration>
package net.codejava.hibernate; import java.util.HashSet; import java.util.Set; 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 how to use JPA annotations to map * a one-to-many association on join table in Hibernate. * @author www.codejava.net * */ public class ArticlesManager { 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(); Category category = new Category("Hibernate Framework"); Article articleOne = new Article("One-to-One Mapping", "One-to-One XML Mapping Tutorial", "Hibernate,One-to-One", "Content of One-to-One XML Mapping Tutorial"); Article articleTwo = new Article("One-to-Many Mapping", "One-to-Many XML Mapping Tutorial", "Hibernate,One-to-Many", "Content of One-to-Many XML Mapping Tutorial"); Article articleThree = new Article("Many-to-Many Mapping", "Many-to-Many XML Mapping Tutorial", "Hibernate,Many-to-Many", "Content of Many-to-Many XML Mapping Tutorial"); Set<Article> articles = new HashSet<Article>(); articles.add(articleOne); articles.add(articleTwo); articles.add(articleThree); category.setArticles(articles); session.save(category); session.getTransaction().commit(); session.close(); } }Output of the program:
Hibernate: insert into CATEGORY (name) values (?) Hibernate: insert into ARTICLE (content, description, keywords, title) values (?, ?, ?, ?) Hibernate: insert into ARTICLE (content, description, keywords, title) values (?, ?, ?, ?) Hibernate: insert into ARTICLE (content, description, keywords, title) values (?, ?, ?, ?) Hibernate: insert into CATEGORY_ARTICLE (CATEGORY_ID, ARTICLE_ID) values (?, ?) Hibernate: insert into CATEGORY_ARTICLE (CATEGORY_ID, ARTICLE_ID) values (?, ?) Hibernate: insert into CATEGORY_ARTICLE (CATEGORY_ID, ARTICLE_ID) values (?, ?)Result in the category table:Result in the article table:Result in the join table category_article:For your reference, you can check the code on GitHub, or download the sample project attached below.