多対多のマッピングによるHibernateの結合テーブル

今日は、XMLとアノテーションの設定を使用したHibernateの多対多マッピングを見ていきます。以前は、Hibernateでの一対一と一対多のマッピングの実装方法を見てきました。

ヒベルネートのマニートゥマニー

多対多のマッピングは、通常、データベースでJoinテーブルを使用して実装されます。例えば、CartテーブルとItemテーブル、そして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;

以下の図は、これらのテーブル間のエンティティ関係を示しています。Cart_Itemsテーブルには余分な列がないことに気付いてください。実際には、多対多のマッピングテーブルには余分な列を持つことはほとんど意味がありません。ただし、余分な列がある場合は、実装が少し変わりますので、別の投稿で説明します。データベースのセットアップは完了しましたので、次にHibernateの多対多のマッピングプロジェクトを作成しましょう。

多対多のマッピングプロジェクト構造をハイバネートする。

Eclipseまたはお気に入りのIDEでMavenプロジェクトを作成し、下の画像にはアプリケーションの構造とさまざまなコンポーネントが表示されています。まず、XMLベースのマッピング実装を調べ、それからJPAアノテーションを使用する方法に移ります。

ヒバネイト・マヴェンの依存関係

私たちの最終的なpom.xmlには、最新バージョン4.3.5.FinalのHibernateの依存関係と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.scdev.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>

ハイバネイトの多対多のXML設定モデルクラスを抑制

カート.java

package com.scdev.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;
	}

}

アイテム.java

package com.scdev.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;
	}
}

カートはアイテムのセットを持ち、アイテムはカートのセットを持っていることに注意してください。これにより、双方向の関連を実装しています。つまり、カートを保存するときにアイテムも保存するように設定できるということですし、その逆も可能です。片方向のマッピングの場合は、通常、モデルクラスのいずれかにセットを使用します。片方向のマッピングにはアノテーションを使用します。

ネイティブな日本語で以下の文を言い換えてください。オプションは1つだけ必要です:
HibernateのMany To ManyマッピングのXML設定。

カートとアイテムの間に発生するHibernateの多対多マッピングの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.scdev.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_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.scdev.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>

上記からご覧いただけるように、マッピングはカートのマッピング構成に非常に似ています。

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">scdev123</property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
		<property name="hibernate.connection.username">scdev</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.scdev.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の情報が保存されることも確認するために、2つのプログラムを書きます。もう1つは商品データを保存し、対応するCartとCart_Itemsが保存されることを確認します。HibernateManyToManyMain.java

package com.scdev.hibernate.main;

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

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

import com.scdev.hibernate.model.Cart;
import com.scdev.hibernate.model.Item;
import com.scdev.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の多対多マッピングの例プログラムを実行すると、以下の出力が得られます。

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

最初のカートを通じてアイテムデータが保存されると、アイテムIDが生成されますが、2番目のカートを保存する際には再度保存されません。もう一つ重要な点は、トランザクションをコミットする際には、多対多の結合テーブルのデータも保存されることです。トランザクションのロールバックを選択した場合に、パフォーマンスを向上させるために行われます。HibernateBiDirectionalManyToManyMain.java

package com.scdev.hibernate.main;

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

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

import com.scdev.hibernate.model.Cart;
import com.scdev.hibernate.model.Item;
import com.scdev.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

以前のテストプログラムと関連付けることが簡単です。双方向のマッピングを設定しているため、ItemまたはCartを保存することができ、マップされたデータは自動的に保存されます。

アノテーションを使用して多対多のマッピングをハイバネイトする

ヒベルネートのxml設定を使用した多対多のマッピングの構成方法を見てきたので、注釈を使用して実装する例を見てみましょう。JPAの注釈を使用して単方向の多対多のマッピングを実装します。

ヒベルネート設定のXMLファイル

私たちのアノテーションベースの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">scdev123</property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
		<property name="hibernate.connection.username">scdev</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.scdev.hibernate.model.Cart1" />
		<mapping class="com.scdev.hibernate.model.Item1" />
	</session-factory>
</hibernate-configuration>

ネイティブの日本語に以下の文を言い換えてください、オプションは1つだけです:Hibernate SessionFactoryユーティリティクラス

下記のようなSessionFactoryを作成するためのユーティリティクラスがあります。HibernateAnnotationUtil.java

package com.scdev.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;
    }
}

多対多マッピングアノテーションモデルクラスについて、ヒバネートを使用すること。

これは、アノテーションに基づいたマッピングの最も重要な部分です。まずはItemテーブルのモデルクラスを見て、その後でCartテーブルのモデルクラスを調べましょう。Item1.java

package com.scdev.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
}

アイテム1のクラスはシンプルで、ここにはリレーショナルマッピングはありません。 Cart1.java

package com.scdev.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アノテーションの使用です。ManyToManyマッピングに使用するテーブル名とカラムを指定しています。

多対多の関連付けを持つHibernateのアノテーションマッピングのテストプログラム

ここに、私たちのハイバネートの多対多マッピングアノテーションベースの構成用の簡単なテストプログラムがあります。HibernateManyToManyAnnotationMain.java

package com.scdev.hibernate.main;

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

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

import com.scdev.hibernate.model.Cart1;
import com.scdev.hibernate.model.Item1;
import com.scdev.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

カートを保存すると、アイテムとカートアイテムのテーブルにもデータが保存されていることは明らかです。アイテム情報のみ保存する場合、カートとカートアイテムのデータが保存されていないことに気づくでしょう。これでHibernateの多対多のマッピングの例のチュートリアルは以上です。以下のリンクからサンプルプロジェクトをダウンロードして試してみて、さらに学んでください。

HibernateのManyToManyマッピングプロジェクトをダウンロードしてください。

コメントを残す 0

Your email address will not be published. Required fields are marked *