웹사이트 검색

하이버네이트 다대다 매핑 - 조인 테이블


오늘 우리는 XML 및 주석 구성을 사용하여 Hibernate Many to Many Mapping을 살펴볼 것입니다. 이전에 우리는 Hibernate에서 일대다 매핑을 구현하는 방법을 살펴보았습니다.

다대다 최대 절전 모드

다대다 매핑은 일반적으로 조인 테이블을 사용하여 데이터베이스에서 구현됩니다. 예를 들어 다대다 매핑을 위해 CartItem 테이블과 Cart_Items 테이블을 가질 수 있습니다. 모든 카트에는 여러 항목이 있을 수 있고 모든 항목은 여러 카트의 일부일 수 있으므로 다대다 매핑이 있습니다.

하이버네이트 다대다 매핑 데이터베이스 설정

아래 스크립트는 다대다 예제 데이터베이스 테이블을 만드는 데 사용할 수 있으며, 이 스크립트는 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;

하이버네이트 다대다 매핑 프로젝트 구조

Hibernate Maven 종속성

최종 pom.xml에는 최신 버전 4.3.5.Final 및 mysql 드라이버 종속성과 함께 Hibernate 종속성이 포함되어 있습니다. 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 구성 모델 클래스

카트.자바

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;
	}

}

항목.자바

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 세트가 있습니다. 이러한 방식으로 양방향 연결을 구현합니다. 장바구니를 저장할 때 항목을 저장하도록 구성하거나 그 반대로 구성할 수 있음을 의미합니다. 단방향 매핑의 경우 일반적으로 모델 클래스 중 하나에 설정했습니다. 단방향 매핑에 주석을 사용합니다.

Hibernate 다대다 매핑 XML 구성

Cart 및 Item에 대한 다대다 매핑 xml 구성 파일을 최대 절전 모드로 만들어 봅시다. 양방향 다대다 매핑을 구현할 것입니다. 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 클래스 item_id 열을 사용합니다. 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 매핑 구성과 매우 유사합니다.

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>

XML 기반 매핑을 위한 Hibernate SessionFactory 유틸리티 클래스

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 {
			// Create the SessionFactory from 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의 팩토리로 작동하는 간단한 유틸리티 클래스입니다.

Hibernate 다대다 매핑 XML 구성 테스트 프로그램

하이버네이트 다대다 매핑 설정이 준비되었습니다. 테스트해 보겠습니다. 우리는 두 가지 프로그램을 작성할 것입니다. 하나는 Cart를 저장하고 Item 및 Cart_Items 정보도 저장되는지 확인하는 것입니다. 다른 하나는 항목 데이터를 저장하고 해당 Cart 및 Cart_Items가 저장되었는지 확인하는 것입니다. 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<Item> items = new HashSet<Item>();
		items.add(iphone); items.add(ipod);
		
		Cart cart = new Cart();
		cart.setItems(items);
		cart.setTotal(150);
		
		Cart cart1 = new Cart();
		Set<Item> items1 = new HashSet<Item>();
		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 many to many 매핑 예제 프로그램을 실행하면 다음과 같은 결과를 얻습니다.

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

첫 번째 장바구니를 통해 상품 데이터를 저장하면 item_id가 생성되며 두 번째 장바구니를 저장하는 동안에는 다시 저장되지 않습니다. 주목해야 할 또 다른 중요한 점은 트랜잭션을 커밋할 때 다대다 조인 테이블 데이터가 저장된다는 것입니다. 트랜잭션을 롤백하기로 선택한 경우 더 나은 성능을 위해 수행됩니다. 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<Cart> cartSet = new HashSet<Cart>();
		cartSet.add(cart);cartSet.add(cart1);
		
		Set<Cart> cartSet1 = new HashSet<Cart>();
		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 다대다 매핑 주석

hibernate xml 구성을 사용하여 다대다 매핑을 구성하는 방법을 살펴보았으니 이제 주석을 통해 이를 구현하는 예를 살펴보겠습니다. JPA 주석을 사용하여 단방향 다대다 매핑을 구현합니다.

Hibernate 구성 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>

Hibernate 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 {
            // Create the SessionFactory from 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 다대다 매핑 주석 모델 클래스

어노테이션 기반 매핑에서 가장 중요한 부분으로, 먼저 Item 테이블 모델 클래스를 살펴본 다음 Cart 테이블 모델 클래스를 살펴보겠습니다. 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 methods
}

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<Item1> items;

//Getter Setter methods
}

여기서 가장 중요한 부분은 다대다 매핑에 사용할 테이블 이름과 열을 제공하는 ManyToMany 주석 및 JoinTable 주석을 사용하는 것입니다.

최대 절전 모드 다대다 주석 매핑 테스트 프로그램

다음은 하이버네이트 다대다 매핑 주석 기반 구성을 위한 간단한 테스트 프로그램입니다. 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 Many-To-Many 매핑 예제 튜토리얼의 전부입니다. 아래 링크에서 샘플 프로젝트를 다운로드하고 이를 가지고 놀면서 자세히 알아볼 수 있습니다.

Hibernate ManyToMany 매핑 프로젝트 다운로드