דוגמה להתאמה של הוברנייט עם יחס One To Many באנוטציות

היום נבחן את המיפוי של One To Many ב-Hibernate. נביט בדוגמא של מיפוי Hibernate One To Many באמצעות הערות ותצורת XML.

One To Many Mapping ב-Hibernate

במונחים פשוטים, מיפוי one to many אומר ששורה אחת בטבלה יכולה להתמפתח למספר שורות בטבלה אחרת. לדוגמה, דמיינו מערכת עגלה שבה יש טבלה נוספת לפריטים. עגלה יכולה לכלול מספר פריטים, ולכן כאן יש מיפוי one to many. נשתמש בתרחיש Cart-Items כדי להמחיש את דוגמת המיפוי שלנו ב-Hibernate.

One To Many Mapping ב-Hibernate – הגדרת מסד נתונים

נוכל להשתמש באילוץ מפתח זר עבור מיפוי one to many. למטה נמצא סקריפט מסד הנתונים שלנו עבור טבלאות Cart ו-Items. אני משתמש במסד נתונים של MySQL כדי להמחיש את דוגמת המיפוי one to many של Hibernate. setup.sql

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

CREATE TABLE `Items` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cart_id` int(11) unsigned NOT NULL,
  `item_id` varchar(10) NOT NULL,
  `item_total` decimal(10,0) NOT NULL,
  `quantity` int(3) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `cart_id` (`cart_id`),
  CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

למטה נמצא תרשים ה-ER של טבלאות העגלה והפריטים. התקנת בסיס הנתונים שלנו מוכנה, בואו נמשיך ליצירת דוגמת פרויקט דוגמה של התאמה אחת לרבים בהיברנייט. למעשה, נשתמש תחילה בתצורה מבוססת XML ואז נממש התאמה של אחד לרבים באמצעות היברנייט ואננוטציות JPA.

מבנה הפרויקט של התאמה אחת לרבים בהיברנייט

צור פרויקט Maven פשוט ב-Eclipse או בסביבת הפיתוח המועדפת עליך, מבנה הפרויקט הסופי ייראה כמו בתמונה למטה.

תלויות היברנייט ב-Maven

קובץ ה-pom.xml הסופי שלנו מכיל תלויות עבור היברנייט ונהג MySQL. היברנייט משתמש ב-JBoss logging והוא מתווסף אוטומטית כתלות התלות.

<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>HibernateOneToManyMapping</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 4.3.5.Final ובגרסת מנהל MySQL המבוססת על התקנת בסיס הנתונים שלי.

מחלקות דגמות One To Many של Hibernate

לטבלאות שלנו, Cart ו-Items, יש מחלקות דגמות לשם השקפתם. Cart.java

package com.journaldev.hibernate.model;

import java.util.Set;

public class Cart {

	private long id;
	private double total;
	private String name;
	private Set<Items> items;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public double getTotal() {
		return total;
	}
	public void setTotal(double total) {
		this.total = total;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Set<Items> getItems() {
		return items;
	}
	public void setItems(Set<Items> items) {
		this.items = items;
	}
	
}

I am using Set of Items, so that every record is unique. We can also use List or Array for one to many mapping in hibernate. Items.java

package com.journaldev.hibernate.model;

public class Items {

	private long id;
	private String itemId;
	private double itemTotal;
	private int quantity;
	private Cart cart;
	
	// Hibernate דורש בנאי ללא פרמטרים
	public Items(){}
	
	public Items(String itemId, double total, int qty, Cart c){
		this.itemId=itemId;
		this.itemTotal=total;
		this.quantity=qty;
		this.cart=c;
	}
	public String getItemId() {
		return itemId;
	}
	public void setItemId(String itemId) {
		this.itemId = itemId;
	}
	public double getItemTotal() {
		return itemTotal;
	}
	public void setItemTotal(double itemTotal) {
		this.itemTotal = itemTotal;
	}
	public int getQuantity() {
		return quantity;
	}
	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}
	public Cart getCart() {
		return cart;
	}
	public void setCart(Cart cart) {
		this.cart = cart;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
}

Items יש יחס רבים לאחד עם Cart, כך שאין צורך להיות לנו אוסף עבור אובייקט Cart.

מחלקת יעודי התועלת של Hibernate SessionFactory

יש לנו מחלקת יעודי ליצירת SessionFactory של Hibernate. 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;
    }
}

קובץ 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="items.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

דוגמה למיפוי חד לרב – הגדרת XML של Hibernate

זהו החלק החשוב ביותר של המדריך, בואו נראה כיצד עלינו לבצע מיפוי של שני המחלקות Cart ו-Items עבור מיפוי חד לרב ב-Hibernate. 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 name="total" />
		</property>
		<property name="name" type="string">
			<column name="name" />
		</property>
		<set name="items" table="ITEMS" fetch="select">
			<key>
				<column name="cart_id" not-null="true"></column>
			</key>
			<one-to-many class="Items"/>
		</set>
	</class>
	
</hibernate-mapping>

החלק החשוב הוא הרכיב set והרכיב one-to-many בתוך זה. שימו לב שאנו מספקים מפתח לשימוש במיפוי חד לרב, כלומר cart_id. items.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="Items" table="ITEMS">
		<id name="id" type="long">
			<column name="id" />
			<generator class="identity" />
		</id>
		<property name="itemId" type="string">
			<column name="item_id"></column>
		</property>
		<property name="itemTotal" type="double">
			<column name="item_total"></column>
		</property>
		<property name="quantity" type="integer">
			<column name="quantity"></column>
		</property>
		
		<many-to-one name="cart" class="Cart">
			<column name="cart_id" not-null="true"></column>
		</many-to-one>
	</class>

</hibernate-mapping>

שימו לב שמתוך פריטים לעגלה, זו יחס של רב לאחד. לכן עלינו להשתמש ברכיב many-to-one עבור עגלה ואנו מספקים שם עמודה שתופקד עם המפתח. לכן, על סמך התצורה של מיפוי ה-Hibernate של העגלה, המפתח שלו cart_id יופקד למיפוי. הפרויקט שלנו עבור דוגמה למיפוי חד לרב ב-Hibernate באמצעות מיפוי XML הוא מוכן, בואו נכתוב תוכנית בדיקה ונבדוק אם היא עובדת כראוי או לא.

דוגמה למיפוי חד לרב – תוכנית בדיקה

HibernateOneToManyMain.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.Items;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateOneToManyMain {

	public static void main(String[] args) {

		Cart cart = new Cart();
		cart.setName("MyCart");
		
		Items item1 = new Items("I1", 10, 1, cart);
		Items item2 = new Items("I2", 20, 2, cart);
		Set itemsSet = new HashSet();
		itemsSet.add(item1); itemsSet.add(item2);
		
		cart.setItems(itemsSet);
		cart.setTotal(10*1 + 20*2);
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//קבל ישיבה
		sessionFactory = HibernateUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created");
		//התחל עסקה
		tx = session.beginTransaction();
		
		//שמור עצם המודל
		session.save(cart);
		session.save(item1);
		session.save(item2);
		
		//בצע עסקה
		tx.commit();
		System.out.println("Cart ID="+cart.getId());
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

}

שים לב שעלינו לשמור כל אובייקטי הסל והפריטים אחד ליד השני. Hibernate יטפל בעדכון המפתחות הזרים בטבלת הפריטים. כאשר אנו מפעילים את התוכנית לעיל, אנו מקבלים את הפלט הבא.

Hibernate Configuration loaded
Hibernate serviceRegistry created
Session created
Hibernate: insert into CART (total, name) values (?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: update ITEMS set cart_id=? where id=?
Hibernate: update ITEMS set cart_id=? where id=?
Cart ID=6
Closing SessionFactory

שים לב ש-Hibernate משתמש בשאילתת עדכון כדי להגדיר את cart_id בטבלת ITEMS.

מיפוי חד לרב ב-Hibernate באמצעות הוספת תיוג

עכשיו שראינו איך ליישם מיפוי חד לרב ב-Hibernate באמצעות הגדרות XML, נראה איך אפשר לעשות אותו הדבר באמצעות הערות JPA.

דוגמה למיפוי חד לרב ב-Hibernate באמצעות הוספת תיוג

קובץ התצורה של Hibernate דומה כמעט, רק שאלמנט המיפוי משתנה משום שאנו משתמשים במחלקות עבור מיפוי חד לרב ב-Hibernate באמצעות הוספת תיוג. 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.Items1"/>
    </session-factory>
</hibernate-configuration>

מחלקת יישות הספרייה של Hibernate

מחלקת יישות הספרייה דומה כמעט, נצטרך רק להשתמש בקובץ הקונפיגורציה החדש של היברנייט. 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 {
            // יצירת המחלקה מ־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;
    }
}

העתקת המיפוי יחד אחת של Hibernate דרך אנוטציות למחלקות מודל

מאחר שאין לנו קבצי מיפוי בעלי תבנית XML, כל ההגדרות הקשורות למיפוי יתבצעו באמצעות אנוטציות JPA במחלקות המודל. אם אתה מבין את המיפוי הבוסס על XML, זה פשוט מאוד ודומה. Cart1.java

package com.journaldev.hibernate.model;

import java.util.Set;

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

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

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="cart_id")
	private long id;
	
	@Column(name="total")
	private double total;
	
	@Column(name="name")
	private String name;
	
	@OneToMany(mappedBy="cart1")
	private Set items1;
	
// שיטות Getter Setter עבור התכונות
}

נקודה חשובה לשים לב אליה היא האנוטציה OneToMany שבה משתמשים במשתנה mappedBy כדי להגדיר את המאפיין במחלקת Items1 שישמש לצורך המיפוי. לכן עלינו להגדיר תכונה בשם "cart1" במחלקת Items1. אל תשכח לכלול את כל שיטות הקבלה והשליחה. Items1.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.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="ITEMS")
public class Items1 {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id")
	private long id;
	
	@Column(name="item_id")
	private String itemId;
	
	@Column(name="item_total")
	private double itemTotal;
	
	@Column(name="quantity")
	private int quantity;
	
	@ManyToOne
	@JoinColumn(name="cart_id", nullable=false)
	private Cart1 cart1;
	
	// Hibernate מחייב בנאי ללא פרמטרים
	public Items1(){}
	
	public Items1(String itemId, double total, int qty, Cart1 c){
		this.itemId=itemId;
		this.itemTotal=total;
		this.quantity=qty;
		this.cart1=c;
	}
// שיטות Getter Setter
}

הנקודה החשובה ביותר במחלקה שלמעלה היא האנוטציה ManyToOne על משתנה המחלקה Cart1 והאנוטציה JoinColumn לציון שם העמודה למיפוי. זהו כל הדבר במיפוי של יחס של אחד לרבים בהיברנייט באמצעות אנוטציות במחלקות המודל. כאשר משווים את זה עם הגדרות XML, תמצאו אותם דומים מאוד. בואו נכתוב תוכנית בדיקה ונפעיל אותה.

דוגמת תוכנית בדיקה למיפוי אנוטציות של יחס של אחד לרבים בהיברנייט

התוכנית הבדיקה שלנו דומה להגדרות XML, אנו פשוט משתמשים במחלקות החדשות כדי לקבל את הסשן של היברנייט ולשמור את אובייקטי המודל במסד הנתונים. HibernateOneToManyAnnotationMain.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.Items1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;

public class HibernateOneToManyAnnotationMain {

	public static void main(String[] args) {

		Cart1 cart = new Cart1();
		cart.setName("MyCart1");
		
		Items1 item1 = new Items1("I10", 10, 1, cart);
		Items1 item2 = new Items1("I20", 20, 2, cart);
		Set itemsSet = new HashSet();
		itemsSet.add(item1); itemsSet.add(item2);
		
		cart.setItems1(itemsSet);
		cart.setTotal(10*1 + 20*2);
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//קבלת הסשן
		sessionFactory = HibernateAnnotationUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created");
		//התחלת העסקה
		tx = session.beginTransaction();
		//שמירת אובייקט המודל
		session.save(cart);
		session.save(item1);
		session.save(item2);
		//סיום העסקה
		tx.commit();
		System.out.println("Cart1 ID="+cart.getId());
		System.out.println("item1 ID="+item1.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
		System.out.println("item2 ID="+item2.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

}

כאשר אנו מפעילים את תוכנית הבדיקה לדוגמת המיפוי של יחס של אחד לרבים בהיברנייט עם אנוטציות, אנו מקבלים את הפלט הבא.

Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Session created
Hibernate: insert into CART (name, total) values (?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Cart1 ID=7
item1 ID=9, Foreign Key Cart ID=7
item2 ID=10, Foreign Key Cart ID=7
Closing SessionFactory

זהו כל הדבר במיפוי של יחס של אחד לרבים בהיברנייט, ניתן להוריד את הפרויקט הדוגמא מהקישור למטה ולבצע ניסויים נוספים.

הורדת פרויקט מיפוי של יחס של אחד לרבים בהיברנייט

Source:
https://www.digitalocean.com/community/tutorials/hibernate-one-to-many-mapping-annotation