Hibernate – 多対多の例 – join table + extra column(Annotation)
このチュートリアルでは、Hibernateを使用して「多対多のテーブル関係、
結合テーブルの
余分の列」を実装する方法を説明します。
1.多対多テーブルの結合テーブルの余分な列
STOCKとCATEGORYの多対多の関係は、STOCK
CATEGORYという名前の第3の/結合テーブルと、余分な “created
by`”列と “created__date`”列でリンクされています。

MySQLテーブルスクリプト
CREATE TABLE `stock` (
`STOCK__ID` INT(10) UNSIGNED NOT NULL AUTO__INCREMENT,
`STOCK__CODE` VARCHAR(10) NOT NULL,
`STOCK__NAME` VARCHAR(20) NOT NULL,
PRIMARY KEY (`STOCK__ID`) USING BTREE,
UNIQUE KEY `UNI__STOCK__NAME` (`STOCK__NAME`),
UNIQUE KEY `UNI__STOCK__ID` (`STOCK__CODE`) USING BTREE
)
CREATE TABLE `category` (
`CATEGORY__ID` INT(10) UNSIGNED NOT NULL AUTO__INCREMENT,
`NAME` VARCHAR(10) NOT NULL,
`DESC` VARCHAR(255) NOT NULL,
PRIMARY KEY (`CATEGORY__ID`) USING BTREE
)
CREATE TABLE `stock__category` (
`STOCK__ID` INT(10) UNSIGNED NOT NULL,
`CATEGORY__ID` INT(10) UNSIGNED NOT NULL,
`CREATED__DATE` DATE NOT NULL,
`CREATED__BY` VARCHAR(10) NOT NULL,
PRIMARY KEY (`STOCK__ID`,`CATEGORY__ID`),
CONSTRAINT `FK__CATEGORY__ID` FOREIGN KEY (`CATEGORY__ID`)
REFERENCES `category` (`CATEGORY__ID`),
CONSTRAINT `FK__STOCK__ID` FOREIGN KEY (`STOCK__ID`)
REFERENCES `stock` (`STOCK__ID`)
)
2.プロジェクトの構成
このチュートリアルのファイルプロジェクトの構造を見直してください。

3. Hibernate/JPAアノテーション
Hibernate/JBossツールで生成された注釈コードは、この第3テーブルの追加の列シナリオでは機能しません。それを動作させるには、
` @ AssociationOverride
“を使用するようにコードをカスタマイズし、` `StockCategory.java`で多対多関係を表現する必要があります。
下記のカスタマイズされたコードをご覧ください:
File:Stock.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
@UniqueConstraint(columnNames = "STOCK__NAME"),
@UniqueConstraint(columnNames = "STOCK__CODE") })
public class Stock implements java.io.Serializable {
private Integer stockId;
private String stockCode;
private String stockName;
private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);
public Stock() {
}
public Stock(String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
public Stock(String stockCode, String stockName,
Set<StockCategory> stockCategories) {
this.stockCode = stockCode;
this.stockName = stockName;
this.stockCategories = stockCategories;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "STOCK__ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
@Column(name = "STOCK__CODE", unique = true, nullable = false, length = 10)
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
@Column(name = "STOCK__NAME", unique = true, nullable = false, length = 20)
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.stock", cascade=CascadeType.ALL)
public Set<StockCategory> getStockCategories() {
return this.stockCategories;
}
public void setStockCategories(Set<StockCategory> stockCategories) {
this.stockCategories = stockCategories;
}
}
__File:Stock Category.java
package com.mkyong.stock;
import java.util.Date;
import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
@Entity
@Table(name = "stock__category", catalog = "mkyongdb")
@AssociationOverrides({
@AssociationOverride(name = "pk.stock",
joinColumns = @JoinColumn(name = "STOCK__ID")),
@AssociationOverride(name = "pk.category",
joinColumns = @JoinColumn(name = "CATEGORY__ID")) })
public class StockCategory implements java.io.Serializable {
private StockCategoryId pk = new StockCategoryId();
private Date createdDate;
private String createdBy;
public StockCategory() {
}
@EmbeddedId
public StockCategoryId getPk() {
return pk;
}
public void setPk(StockCategoryId pk) {
this.pk = pk;
}
@Transient
public Stock getStock() {
return getPk().getStock();
}
public void setStock(Stock stock) {
getPk().setStock(stock);
}
@Transient
public Category getCategory() {
return getPk().getCategory();
}
public void setCategory(Category category) {
getPk().setCategory(category);
}
@Temporal(TemporalType.DATE)
@Column(name = "CREATED__DATE", nullable = false, length = 10)
public Date getCreatedDate() {
return this.createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
@Column(name = "CREATED__BY", nullable = false, length = 10)
public String getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
StockCategory that = (StockCategory) o;
if (getPk() != null ? !getPk().equals(that.getPk())
: that.getPk() != null)
return false;
return true;
}
public int hashCode() {
return (getPk() != null ? getPk().hashCode() : 0);
}
}
File:StockCategoryId.java
package com.mkyong.stock;
import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;
@Embeddable
public class StockCategoryId implements java.io.Serializable {
private Stock stock;
private Category category;
@ManyToOne
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
@ManyToOne
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StockCategoryId that = (StockCategoryId) o;
if (stock != null ? !stock.equals(that.stock) : that.stock != null) return false;
if (category != null ? !category.equals(that.category) : that.category != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (stock != null ? stock.hashCode() : 0);
result = 31 ** result + (category != null ? category.hashCode() : 0);
return result;
}
}
File:Category.java
package com.mkyong.stock;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {
private Integer categoryId;
private String name;
private String desc;
private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);
public Category() {
}
public Category(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Category(String name, String desc, Set<StockCategory> stockCategories) {
this.name = name;
this.desc = desc;
this.stockCategories = stockCategories;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "CATEGORY__ID", unique = true, nullable = false)
public Integer getCategoryId() {
return this.categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
@Column(name = "NAME", nullable = false, length = 10)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "[DESC]", nullable = false)
public String getDesc() {
return this.desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.category")
public Set<StockCategory> getStockCategories() {
return this.stockCategories;
}
public void setStockCategories(Set<StockCategory> stockCategories) {
this.stockCategories = stockCategories;
}
}
完了したら、多対多の関係が今働いているはずです。
4.実行 – ケース1
新しいカテゴリーと新しい在庫のため。
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
//new category, need save to get the id first
session.save(category1);
StockCategory stockCategory = new StockCategory();
stockCategory.setStock(stock);
stockCategory.setCategory(category1);
stockCategory.setCreatedDate(new Date());//extra column
stockCategory.setCreatedBy("system");//extra column
stock.getStockCategories().add(stockCategory);
session.save(stock);
session.getTransaction().commit();
出力…
Hibernate:
insert
into
mkyongdb.category
(`DESC`, NAME)
values
(?, ?)
Hibernate:
insert
into
mkyongdb.stock
(STOCK__CODE, STOCK__NAME)
values
(?, ?)
Hibernate:
select
stockcateg__.CATEGORY__ID,
stockcateg__.STOCK__ID,
stockcateg__.CREATED__BY as CREATED1__2__,
stockcateg__.CREATED__DATE as CREATED2__2__
from
mkyongdb.stock__category stockcateg__
where
stockcateg__.CATEGORY__ID=?
and stockcateg__.STOCK__ID=?
Hibernate:
insert
into
mkyongdb.stock__category
(CREATED__BY, CREATED__DATE, CATEGORY__ID, STOCK__ID)
values
(?, ?, ?, ?)
5.それを実行する – ケース2
既存のカテゴリと新しい在庫を取得します。
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
//assume category id is 7
Category category1 = (Category)session.get(Category.class, 7);
StockCategory stockCategory = new StockCategory();
stockCategory.setStock(stock);
stockCategory.setCategory(category1);
stockCategory.setCreatedDate(new Date());//extra column
stockCategory.setCreatedBy("system");//extra column
stock.getStockCategories().add(stockCategory);
session.save(stock);
session.getTransaction().commit();
出力…
Hibernate:
select
category0__.CATEGORY__ID as CATEGORY1__1__0__,
category0__.`DESC` as DESC2__1__0__,
category0__.NAME as NAME1__0__
from
mkyongdb.category category0__
where
category0__.CATEGORY__ID=?
Hibernate:
insert
into
mkyongdb.stock
(STOCK__CODE, STOCK__NAME)
values
(?, ?)
Hibernate:
select
stockcateg__.CATEGORY__ID,
stockcateg__.STOCK__ID,
stockcateg__.CREATED__BY as CREATED1__2__,
stockcateg__.CREATED__DATE as CREATED2__2__
from
mkyongdb.stock__category stockcateg__
where
stockcateg__.CATEGORY__ID=?
and stockcateg__.STOCK__ID=?
Hibernate:
insert
into
mkyongdb.stock__category
(CREATED__BY, CREATED__DATE, CATEGORY__ID, STOCK__ID)
values
(?, ?, ?, ?)
完了しました。
ダウンロードする – リンク://wp-content/uploads/2011/04/Hibernate-many-to-many-third-table-annotation.zip[Hibernate-many-to-many-third-table-annotation.zip]( 13KB)
リファレンス
マッピングドキュメンテーション]