Hibernate 多对多映射 - 连接表
今天,我们将研究使用 XML 和注释配置的Hibernate 多对多映射。之前,我们研究了如何在 Hibernate 中实现一对一和一对多映射。
Hibernate 多对多
多对多映射通常使用连接表在数据库中实现。例如,我们可以有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 表没有任何额外的列,实际上在多对多映射表中有额外的列没有多大意义。但是如果你有额外的列,实现会稍有变化,我们将在另一篇文章中讨论这个问题。下图显示了这些表之间的实体关系。我们的数据库设置现在已经准备好了,让我们继续创建 Hibernate 多对多映射项目。
Hibernate 多对多映射项目结构
在 Eclipse 或您最喜欢的 IDE 中创建一个 maven 项目,下图显示了应用程序中的结构和不同组件。我们将首先研究基于 XML 的映射实现,然后转向使用 JPA 注释。
Hibernate Maven 依赖项
我们最终的 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.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,这样我们就实现了双向关联。这意味着我们可以将其配置为在保存 Cart 时保存 Item,反之亦然。对于单向映射,通常我们在其中一个模型类中设置。我们将使用注释进行单向映射。
Hibernate 多对多映射 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.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 是键,many-to-many
映射使用 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>
从上面可以看出,该映射与购物车映射配置非常相似。
基于 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配置测试程序
我们的 Hibernate 多对多映射设置已准备就绪,让我们测试一下。我们将编写两个程序,一个用于保存购物车并查看 Item 和 Cart_Items 信息是否也已保存。另一个用于保存项目数据并检查相应的购物车和 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 多对多映射示例程序时,我们得到以下输出。
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 配置文件如下所示。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
,我们在其中提供用于多对多映射的表名和列。
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 多对多映射示例教程的全部内容,您可以从以下链接下载示例项目并试用它以了解更多信息。
下载 Hibernate ManyToMany 映射项目