היום נסתכל על מיפוי רבים לרבים של Hibernate באמצעות תצורות XML והערכה. לפני כן ראינו כיצד ליישם One To One ו- One To Many mapping ב-Hibernate.
Hibernate רבים לרבים
Many-to-Many נפתח בדרך כלל בבסיס נתונים באמצעות טבלת הצטרפות. לדוגמה, יכולים להיות לנו טבלאות Cart
ו- Item
וטבלת Cart_Items
עבור מיפוי רבים לרבים. כל עגלה יכולה להכיל מספר פריטים וכל פריט יכול להיות חלק ממספר עגלות, לכן יש לנו כאן מיפוי רבים לרבים.
הגדרת מסד נתונים למיפוי רבים לרבים של Hibernate
התסריט הבא יכול לשמש ליצירת טבלאות מסד הנתונים של הדוגמה שלנו לרבים-לרבים, התסריטים אלה תוכננו למסד הנתונים MySQL. אם אתה משתמש במסד נתונים אחר, עשויים להיות צורך בשינויים קטנים כדי להפעיל אותו.
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;
שים לב שטבלת Cart_Items אין לה עמודות נוספות, בעצם אין הרבה תועלת לקיים עמודות נוספות בטבלת המיפוי רב לרב. אבל אם יש לך עמודות נוספות, היישום משתנה קצת ונבדוק את זה בפוסט אחר. בתרשים למטה מוצגת הקשריות היחסית בין הטבלאות הללו. הגדרת בסיס הנתונים שלנו מוכנה כעת, נמשיך ליצור את פרויקט המיפוי של הרב לרב של היברנייט.
מבנה פרויקט המיפוי הרב לרב של היברנייט
צור פרויקט מייבן באקליפס או בסביבת הפיתוח האהובה עליך, התמונה למטה מראה את המבנה והרכיבים השונים באפליקציה. נבדוק תחילה את המימושים המבוססים על XML ואז נעבור להשתמש באנוטציות JPA.
תלותי Maven של Hibernate
הַקָּובֶץ שֶׁלֵנוּ בָּ pom.xml הַסוֹפִי מַכְיל תְּלָוֹיוֹת שֶׁל Hibernate עִם גַּרְסַת עִדכוּן הָאַחֲרוֹנָה 4.3.5.Final וּתְלָוֹיוֹת שֶׁל מוֹנְהָ MySQL. 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 רבים אל הרבים בתצורת XML
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;
}
}
שימו לב שֶׁלַ־Cart יש קְבוּצָה שֶׁל Item וְלַItem יש קְבוּצָה שֶׁל Cart, בַּדְרךְ זוֹ אֲנַחְנוּ מְיַישְׁמִים קְשָׁרִים דוֹ כְּיַחֲסָה. זֶה אוֹמֵר שֶׁאֲנַחְנוּ יכולים לְתַצֵפִין אוֹתוֹ לְשָׂמוֹר Item כְּאָשֶׁר אֲנַחְנוּ שׁוֹמְרִים Cart וְלהיפך. לְמַיפוּת כְּיַחֲסָה יַחַידָה, רָגִיל לָנוּ קָבוּע בְּאַחַת מִתוֹךְ מוֹדֵלֵי הַמוּדֵל. נִשְׁתַּמֵּשׁ בְּהַסְוָנוֹת לְמַיפוּת כְּיַחֲסָה יַחַידָה.
מוֹדְלִים רבים אל הרבים שֶׁל Hibernate בתצורת XML Configuration
בואו נִיצוֹר קְבָצִים שֶׁל הַגדָרָה XML לְמַפֵּה הָרַבִּים אֶל הרַבִּים לַCart וְלַItem. נְמַמֵּשׁ מַפֵּה רַבִּים אֶל הרַבִּים דוֹ כְּיַחֲסָה. 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>
שימו לב שקבוצת הפריטים ממופה לטבלת CART_ITEMS. מאחר וְהַCart הוּא הָעִקְרוֹן, cart_id הוּא הַמַפְתֵחַ וּמַפְה רַבִּים אֶל הרַבִּים משתמש בְּעַמּוּד הַitem_id בכיתת Item. 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>
כפי שאתה יכול לראות מהעילה, המיפוי דומה מאוד להגדרות המיפוי של Cart.
הגדרת Hibernate עבור מיפוי רב לרב באמצעות XML
קובץ ההגדרות שלנו ל-Hibernate נראה כך. 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>
מחלקת יעוץ ל-SessionFactory עבור מיפוי באמצעות XML
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 {
// יצירת ה-SessionFactory מתוך 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;
}
}
זו מחלקת יעוץ פשוטה הפועלת כמפענח עבור SessionFactory
.
תוכנית בדיקה של תצורת ה-Mapping של Hibernate רב לרב באמצעות XML
הגדרת המיפוי הרבים לרבים שלנו ב-Hibernate מוכנה, בואו לבדוק אותה. נכתוב שני תוכניות, האחת לשמור עגלה ולוודא שמידע על פריט ועגלה-פריטים נשמר גם. תוכנית נוספת לשמור נתוני פריט ולבדוק שעגלה ועגלה-פריטים יסודרו. 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 {
//שמירת רבים-לרבים כאשר Cart הוא ראשי
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();
}
}
}
כאשר אנו מפעילים את דוגמת המיפוי רבים לרבים ב-Hibernate שלמעלה, אנו מקבלים את הפלט הבא.
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
שימו לב שפעמיים, ברגע שנתוני הפריט נשמרים דרך עגלה הראשונה, המזהה של הפריט נוצר וכאשר אנו שומרים עגלה שנייה, הוא לא נשמר שוב. נקודה חשובה נוספת לשים לב אליה היא שנתוני הטבלה המקושרת רבים-לרבים נשמרים כאשר אנו מכינים את העסקה. נעשה זאת לשיפור בביצועים אילו נבחר לבטל את העסקה. 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 {
//שמירת רבים-לרבים כאשר Item הוא ראשי
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();
}
}
}
פלט של התוכנית למעלה הוא:
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
אתם יכולים בקלות לקשר את זה לתוכנית הבדיקה הקודמת, מאחר והגדרנו מיפוי דו-כיווני יכולנו לשמור פריט או עגלה והמידע מפולט יתמקם באופן אוטומטי.
הערה על המיפוי רבים לרבים ב-Hibernate
עכשיו שראינו איך להגדיר התאמה של רבים לרבים באמצעות תצורות XML של היברנייט, בואו נסתכל על דוגמה ליישום זה דרך הערות. ניישם התאמה רבים-לרבים חד כיוונית באמצעות הערות JPA.
קובץ התצורה של היברנייט XML
הקובץ של ההגדרות שלנו על בסיס הערות היברנייט נראה כמו בהמשך. 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
מחלקת היעוץ שלנו ליצירת SessionFactory נראית כמו בהמשך. 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 {
// יצירת SessionFactory מה- 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;
}
}
מודלי ההתאמה רבים לרבים באמצעות הערות Annotation
זהו החלק החשוב ביותר למיפוי על בסיס הערות, בואו נסתכל תחילה על מודל טבלת הפריט ולאחר מכן נסתכל על מודל טבלת העגלה. 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
}
כיתת Item1 נראית פשוטה, אין כאן מיפוי יחסים. 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
}
החלק החשוב ביותר כאן הוא השימוש בהערה ManyToMany
וההערה JoinTable
שבה אנו מספקים שם טבלה ועמודות המשמשות למיפוי רב לרב.
תוכנית מבחן למיפוי רב לרב באמצעות הערות הובלה של Hibernate
הנה תוכנית מבחן פשוטה עבור הגדרתנו למיפוי רב לרב באמצעות הערות הובלה של Hibernate. 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();
}
}
}
כאשר אנו מפעילים את התוכנית לעיל, היא מייצרת את הפלט הבא.
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
ברור ששמירה של עגלה מבצעת גם שמירה של נתונים בטבלת Item ו- Cart_Items. אם תשמור רק על מידע עגלה, תשים לב שנתוני Cart ו- Cart_Items לא ישמרו. זהו הכל בדוגמה למיפוי רב לרב של Hibernate, תוכל להוריד את הפרויקט הדוגמא מהקישור למטה ולשחק איתו כדי ללמוד נוסף.
Source:
https://www.digitalocean.com/community/tutorials/hibernate-many-to-many-mapping-join-tables