Many To Many Mapping in Hibernate – Join Tables

Vandaag zullen we kijken naar Hibernate Many to Many Mapping met behulp van XML- en annotatieconfiguraties. Eerder hebben we gezien hoe we One To One en One To Many-mapping kunnen implementeren in Hibernate.

Hibernate Many to Many

Many-to-Many-mapping wordt meestal geïmplementeerd in de database met behulp van een Join Table. Bijvoorbeeld, we kunnen de tabellen Cart en Item hebben en de tabel Cart_Items voor many-to-many-mapping. Elke winkelwagen kan meerdere items bevatten en elk item kan deel uitmaken van meerdere winkelwagens, dus we hebben hier een many-to-many-mapping.

Hibernate Many to Many Mapping Database Setup

Onderstaand script kan worden gebruikt om onze many-to-many voorbeelddatabasetabellen te maken. Deze scripts zijn voor MySQL-database. Als u een andere database gebruikt, moet u mogelijk kleine wijzigingen aanbrengen om het te laten werken.

DROP TABLE IF EXISTS `Cart_Items`;
DROP TABLE IF EXISTS `Cart`;
DROP TABLE IF EXISTS `Item`;

CREATE TABLE `Cart` (
  `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cart_total` decimal(10,0) NOT NULL,
  PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `Item` (
  `item_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `item_desc` varchar(20) NOT NULL,
  `item_price` decimal(10,0) NOT NULL,
  PRIMARY KEY (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `Cart_Items` (
  `cart_id` int(11) unsigned NOT NULL,
  `item_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`cart_id`,`item_id`),
  CONSTRAINT `fk_cart` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`),
  CONSTRAINT `fk_item` FOREIGN KEY (`item_id`) REFERENCES `Item` (`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Merk op dat de Cart_Items-tabel geen extra kolommen heeft, eigenlijk heeft het niet veel zin om extra kolommen te hebben in een mappingtabel met een op veel-op-veel-relaties. Maar als je extra kolommen hebt, verandert de implementatie een beetje en zullen we dat in een andere post bekijken. Onderstaande diagram toont de entiteitsrelatie tussen deze tabellen. Onze databasesetup is nu klaar, laten we doorgaan met het maken van het Hibernate many-to-many mappingproject.

Structuur van het Hibernate Many-To-Many Mapping Project

Maak een Maven-project in Eclipse of je favoriete IDE, onderstaande afbeelding toont de structuur en de verschillende componenten in de applicatie. We zullen eerst kijken naar de XML-gebaseerde mappingimplementaties en vervolgens overgaan op het gebruik van JPA-annotaties.

Hibernate Maven-afhankelijkheden

Onze uiteindelijke pom.xml bevat Hibernate afhankelijkheden met de nieuwste versie 4.3.5.Final en mysql driver afhankelijkheden. pom.xml

<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>HibernateManyToManyMapping</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>

Hibernate Many to Many XML Configuratie Model Klassen

Cart.java

package com.journaldev.hibernate.model;

import java.util.Set;

public class Cart {

	private long id;
	private double total;

	private Set<Item> items;
	
	public double getTotal() {
		return total;
	}

	public void setTotal(double total) {
		this.total = total;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public Set<Item> getItems() {
		return items;
	}

	public void setItems(Set<Item> items) {
		this.items = items;
	}

}

Item.java

package com.journaldev.hibernate.model;

import java.util.Set;

public class Item {

	private long id;
	private double price;
	private String description;

	private Set<Cart> carts;
	
	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Set<Cart> getCarts() {
		return carts;
	}

	public void setCarts(Set<Cart> carts) {
		this.carts = carts;
	}
}

Let op dat Cart een set van Item heeft en Item een set van Cart heeft, op deze manier implementeren we Bi-Directionele associaties. Dit betekent dat we het kunnen configureren om Item op te slaan wanneer we Cart opslaan en vice versa. Voor one-directional mapping hebben we meestal een set in een van de modelklassen. We zullen annotaties gebruiken voor one-directional mapping.

Hibernate Many To Many Mapping XML Configuratie

Laten we hibernate many to many mapping xml configuratiebestanden maken voor Cart en Item. We zullen bi-directionele many-to-many mapping implementeren. cart.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 package="com.journaldev.hibernate.model">
	<class name="Cart" table="CART">
		<id name="id" type="long">
			<column name="cart_id" />
			<generator class="identity" />
		</id>
		<property name="total" type="double" column="cart_total" />

		<set name="items" table="CART_ITEMS" fetch="select" cascade="all">
			<key column="cart_id" />
			<many-to-many class="Item" column="item_id" />
		</set>
	</class>

</hibernate-mapping>

Let op dat de set items is toegewezen aan CART_ITEMS-tabel. Aangezien Cart het primaire object is, is cart_id de sleutel en many-to-many mapping gebruikt de item_id kolom van de Item klasse. item.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 package="com.journaldev.hibernate.model">

	<class name="Item" table="ITEM">
		<id name="id" type="long">
			<column name="item_id" />
			<generator class="identity" />
		</id>
		<property name="description" type="string" column="item_desc" />

		<property name="price" type="double" column="item_price" />

		<set name="carts" table="CART_ITEMS" fetch="select" cascade="all">
			<key column="item_id" />
			<many-to-many class="Cart" column="cart_id" />
		</set>

	</class>

</hibernate-mapping>

Zoals je kunt zien, is de mapping zeer vergelijkbaar met Cart mapping-configuraties.

Onze hibernate-configuratiebestand ziet er als volgt uit. 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="cart.hbm.xml" />
		<mapping resource="item.hbm.xml" />
	</session-factory>
</hibernate-configuration>

Hibernate SessionFactory Utility Class voor XML-gebaseerde Mapping

HibernateUtil.java

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 {
			// Maak de SessionFactory aan vanuit 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;
	}

}

Het is een eenvoudige hulpprogrammaklasse die fungeert als een fabriek voor SessionFactory.

Hibernate Many To Many Mapping XML Configuratie Testprogramma

Onze hibernate many-to-many mapping setup is klaar, laten we het testen. We schrijven twee programma’s, één om de Cart op te slaan en te controleren of de Item en Cart_Items informatie ook wordt opgeslagen. De andere om itemgegevens op te slaan en te controleren of de bijbehorende Cart en Cart_Items worden opgeslagen. HibernateManyToManyMain.java

package com.journaldev.hibernate.main;

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

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

import com.journaldev.hibernate.model.Cart;
import com.journaldev.hibernate.model.Item;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateManyToManyMain {
	
	//Saving many-to-many where Cart is primary
	public static void main(String[] args) {
		
		Item iphone = new Item();
		iphone.setPrice(100); iphone.setDescription("iPhone");
		
		Item ipod = new Item();
		ipod.setPrice(50); ipod.setDescription("iPod");
		
		Set items = new HashSet();
		items.add(iphone); items.add(ipod);
		
		Cart cart = new Cart();
		cart.setItems(items);
		cart.setTotal(150);
		
		Cart cart1 = new Cart();
		Set items1 = new HashSet();
		items1.add(iphone);
		cart1.setItems(items1);
		cart1.setTotal(100);
		
		SessionFactory sessionFactory = null;
		try{
		sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.save(cart);
		session.save(cart1);
		System.out.println("Before committing transaction");
		tx.commit();
		sessionFactory.close();
		
		System.out.println("Cart ID="+cart.getId());
		System.out.println("Cart1 ID="+cart1.getId());
		System.out.println("Item1 ID="+iphone.getId());
		System.out.println("Item2 ID="+ipod.getId());
		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()) sessionFactory.close();
		}
		
	}

}

Wanneer we bovenstaand voorbeeldprogramma voor hibernate many-to-many mapping uitvoeren, krijgen we de volgende output.

Hibernate Configuration loaded
Hibernate serviceRegistry created
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into CART (cart_total) values (?)
Before committing transaction
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Cart ID=1
Cart1 ID=2
Item1 ID=1
Item2 ID=2

Merk op dat zodra de itemgegevens zijn opgeslagen via de eerste cart, de item_id wordt gegenereerd en bij het opslaan van de tweede cart niet opnieuw wordt opgeslagen. Een ander belangrijk punt om op te merken is dat de many-to-many join table-gegevens worden opgeslagen wanneer we de transactie committen. Dit wordt gedaan voor betere prestaties in het geval we ervoor kiezen om de transactie terug te draaien. HibernateBiDirectionalManyToManyMain.java

package com.journaldev.hibernate.main;

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

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

import com.journaldev.hibernate.model.Cart;
import com.journaldev.hibernate.model.Item;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateBiDirectionalManyToManyMain {

	//Saving many-to-many where Item is primary
	public static void main(String[] args) {
		
		Item iphone = new Item();
		iphone.setPrice(100); iphone.setDescription("iPhone");
		
		Item ipod = new Item();
		ipod.setPrice(50); ipod.setDescription("iPod");
		
		Cart cart = new Cart();
		cart.setTotal(150);
		
		Cart cart1 = new Cart();
		cart1.setTotal(100);
		
		Set cartSet = new HashSet();
		cartSet.add(cart);cartSet.add(cart1);
		
		Set cartSet1 = new HashSet();
		cartSet1.add(cart);
		
		iphone.setCarts(cartSet1);
		ipod.setCarts(cartSet);
		
		SessionFactory sessionFactory = null;
		try{
		sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.save(iphone);
		session.save(ipod);
		tx.commit();
		sessionFactory.close();
		
		System.out.println("Cart ID="+cart.getId());
		System.out.println("Cart1 ID="+cart1.getId());
		System.out.println("Item1 ID="+iphone.getId());
		System.out.println("Item2 ID="+ipod.getId());
		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()) sessionFactory.close();
		}
		
	}

}

De output van het bovenstaande programma is:

Hibernate Configuration loaded
Hibernate serviceRegistry created
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into CART_ITEMS (item_id, cart_id) values (?, ?)
Hibernate: insert into CART_ITEMS (item_id, cart_id) values (?, ?)
Hibernate: insert into CART_ITEMS (item_id, cart_id) values (?, ?)
Cart ID=3
Cart1 ID=4
Item1 ID=3
Item2 ID=4

Je kunt dit gemakkelijk relateren aan het eerdere testprogramma, aangezien we bi-directionele mapping hebben geconfigureerd, kunnen we Item of Cart opslaan en de bijbehorende gemapte gegevens worden automatisch opgeslagen.

Hibernate Many To Many Mapping Annotation

Nu we hebben gezien hoe we een many-to-many mapping kunnen configureren met behulp van hibernate-xml-configuraties, laten we een voorbeeld bekijken van hoe we dit kunnen implementeren via annotaties. We zullen een eenzijdige many-to-many mapping implementeren met behulp van JPA-annotaties.

Hibernate configuratie XML-bestand

Ons op annotaties gebaseerde hibernate configuratiebestand ziet er als volgt uit. 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.Cart1" />
		<mapping class="com.journaldev.hibernate.model.Item1" />
	</session-factory>
</hibernate-configuration>

SessionFactory hulpprogrammaklasse

Onze hulpprogrammaklasse om SessionFactory te maken ziet er als volgt uit. HibernateAnnotationUtil.java

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 {
            // Maak de SessionFactory aan vanuit 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;
    }
}

Many to Many Mapping Annotatie Model Klassen

Dit is het belangrijkste deel voor annotatiegebaseerde mapping, laten we eerst kijken naar de Item tabel modelklasse en dan zullen we kijken naar de Cart tabel modelklasse. Item1.java

package com.journaldev.hibernate.model;

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

@Entity
@Table(name="ITEM")
public class Item1 {

	@Id
	@Column(name="item_id")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private long id;
	
	@Column(name="item_price")
	private double price;
	
	@Column(name="item_desc")
	private String description;
	
// Getter Setter methoden
}

Item1 klasse ziet er eenvoudig uit, er is hier geen relationele mapping. Cart1.java

package com.journaldev.hibernate.model;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "CART")
public class Cart1 {

	@Id
	@Column(name = "cart_id")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;

	@Column(name = "cart_total")
	private double total;

	@ManyToMany(targetEntity = Item1.class, cascade = { CascadeType.ALL })
	@JoinTable(name = "CART_ITEMS", 
				joinColumns = { @JoinColumn(name = "cart_id") }, 
				inverseJoinColumns = { @JoinColumn(name = "item_id") })
	private Set items;

//Getter Setter methoden
}

Het belangrijkste hier is het gebruik van de ManyToMany annotatie en de JoinTable annotatie waarbij we de tabelnaam en kolommen opgeven die moeten worden gebruikt voor many-to-many mapping.

Hibernate Many To Many Annotation Mapping Test Program

Hier is een eenvoudig testprogramma voor onze hibernate many-to-many mapping annotatie gebaseerde configuratie. HibernateManyToManyAnnotationMain.java

package com.journaldev.hibernate.main;

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

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

import com.journaldev.hibernate.model.Cart1;
import com.journaldev.hibernate.model.Item1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;

public class HibernateManyToManyAnnotationMain {

	public static void main(String[] args) {
		Item1 item1 = new Item1();
		item1.setDescription("samsung"); item1.setPrice(300);
		Item1 item2 = new Item1();
		item2.setDescription("nokia"); item2.setPrice(200);
		Cart1 cart = new Cart1();
		cart.setTotal(500);
		Set<Item1> items = new HashSet<Item1>();
		items.add(item1); items.add(item2);
		cart.setItems(items);
		
		SessionFactory sessionFactory = null;
		try{
		sessionFactory = HibernateAnnotationUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.save(cart);
		System.out.println("Before committing transaction");
		tx.commit();
		sessionFactory.close();
		
		System.out.println("Cart ID="+cart.getId());
		System.out.println("Item1 ID="+item1.getId());
		System.out.println("Item2 ID="+item2.getId());
		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(sessionFactory != null && !sessionFactory.isClosed()) sessionFactory.close();
		}
	}

}

Wanneer we het bovenstaande programma uitvoeren, produceert het de volgende uitvoer.

Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Hibernate: insert into CART (cart_total) values (?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Hibernate: insert into ITEM (item_desc, item_price) values (?, ?)
Before committing transaction
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Hibernate: insert into CART_ITEMS (cart_id, item_id) values (?, ?)
Cart ID=5
Item1 ID=6
Item2 ID=5

Het is duidelijk dat het opslaan van een winkelwagen ook gegevens opslaat in de Item en Cart_Items tabel. Als u alleen iteminformatie opslaat, zult u merken dat de gegevens van Cart en Cart_Items niet worden opgeslagen. Dat is alles voor het Hibernate Many-To-Many mapping voorbeeldtutorial, u kunt het voorbeeldproject downloaden via onderstaande link en ermee spelen om meer te leren.

Download Hibernate ManyToMany Mapping Project

Source:
https://www.digitalocean.com/community/tutorials/hibernate-many-to-many-mapping-join-tables