今天,我们将研究Hibernate中的一对多映射。我们将研究使用注释和XML配置的Hibernate一对多映射示例。
Hibernate中的一对多映射
简而言之,一对多映射是指一张表中的一行可以映射到另一张表中的多行。例如,设想一个购物车系统,其中我们有另一张桌子来存放物品。一个购物车可以有多个项目,所以这里有一对多映射。我们将使用购物车场景作为Hibernate一对多映射示例。
Hibernate-数据库设置中的一对多映射
我们可以使用外键约束 进行一对多映射。下面是我们的Cart
和Items
表的数据库脚本。我是用MySQL数据库进行Hibernate一对多映射的例子。setup.sql
1CREATE TABLE `Cart` (
2 `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
3 `total` decimal(10,0) NOT NULL,
4 `name` varchar(10) DEFAULT NULL,
5 PRIMARY KEY (`cart_id`)
6) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
7
8CREATE TABLE `Items` (
9 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
10 `cart_id` int(11) unsigned NOT NULL,
11 `item_id` varchar(10) NOT NULL,
12 `item_total` decimal(10,0) NOT NULL,
13 `quantity` int(3) NOT NULL,
14 PRIMARY KEY (`id`),
15 KEY `cart_id` (`cart_id`),
16 CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`)
17) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
下面是购物车和物品表的ER图。我们的数据库设置已经准备好了,让我们继续创建Hibernate一对多映射示例项目。首先,我们将使用基于XML的配置,然后我们将使用Hibernate和JPA注释实现一对多映射。
Hibernate一对多映射项目结构
在Eclipse或您喜欢的IDE中创建一个简单的Maven项目,最终的项目结构将如下图所示。
Hibernate Maven依赖
最后的pom.xml文件包含Hibernate和MySQL驱动程序的依赖项。Hibernate使用JBoss日志记录,并自动添加为可传递依赖项。
1<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">
2 <modelVersion>4.0.0</modelVersion>
3 <groupId>com.journaldev.hibernate</groupId>
4 <artifactId>HibernateOneToManyMapping</artifactId>
5 <version>0.0.1-SNAPSHOT</version>
6
7 <dependencies>
8 <dependency>
9 <groupId>org.hibernate</groupId>
10 <artifactId>hibernate-core</artifactId>
11 <version>4.3.5.Final</version>
12 </dependency>
13 <dependency>
14 <groupId>mysql</groupId>
15 <artifactId>mysql-connector-java</artifactId>
16 <version>5.0.5</version>
17 </dependency>
18 </dependencies>
19
20</project>
请注意,我使用的是最新的Hibernate版本4.3.5最终版 和基于我的数据库安装的MySQL驱动程序版本。
Hibernate一对多映射模型类
对于我们的表Cart和Items,我们有模型类来反映它们。Cart.java
1package com.journaldev.hibernate.model;
2
3import java.util.Set;
4
5public class Cart {
6
7 private long id;
8 private double total;
9 private String name;
10 private Set<Items> items;
11
12 public long getId() {
13 return id;
14 }
15 public void setId(long id) {
16 this.id = id;
17 }
18 public double getTotal() {
19 return total;
20 }
21 public void setTotal(double total) {
22 this.total = total;
23 }
24 public String getName() {
25 return name;
26 }
27 public void setName(String name) {
28 this.name = name;
29 }
30 public Set<Items> getItems() {
31 return items;
32 }
33 public void setItems(Set<Items> items) {
34 this.items = items;
35 }
36
37}
我使用的是一组条目,因此每条记录都是唯一的。我们还可以在Hibernate中使用List或数组进行一对多映射。Items.java
1package com.journaldev.hibernate.model;
2
3public class Items {
4
5 private long id;
6 private String itemId;
7 private double itemTotal;
8 private int quantity;
9 private Cart cart;
10
11 //Hibernate requires no-args constructor
12 public Items(){}
13
14 public Items(String itemId, double total, int qty, Cart c){
15 this.itemId=itemId;
16 this.itemTotal=total;
17 this.quantity=qty;
18 this.cart=c;
19 }
20 public String getItemId() {
21 return itemId;
22 }
23 public void setItemId(String itemId) {
24 this.itemId = itemId;
25 }
26 public double getItemTotal() {
27 return itemTotal;
28 }
29 public void setItemTotal(double itemTotal) {
30 this.itemTotal = itemTotal;
31 }
32 public int getQuantity() {
33 return quantity;
34 }
35 public void setQuantity(int quantity) {
36 this.quantity = quantity;
37 }
38 public Cart getCart() {
39 return cart;
40 }
41 public void setCart(Cart cart) {
42 this.cart = cart;
43 }
44 public long getId() {
45 return id;
46 }
47 public void setId(long id) {
48 this.id = id;
49 }
50
51}
商品与购物车有多对一的关系,因此我们不需要拥有购物车对象的集合。
Hibernate SessionFactory实用类
我们有一个用于创建Hibernate SessionFactory的实用程序类。HibernateUtil.java
1package com.journaldev.hibernate.util;
2
3import org.hibernate.SessionFactory;
4import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
5import org.hibernate.cfg.Configuration;
6import org.hibernate.service.ServiceRegistry;
7
8public class HibernateUtil {
9
10 private static SessionFactory sessionFactory;
11
12 private static SessionFactory buildSessionFactory() {
13 try {
14 // Create the SessionFactory from hibernate.cfg.xml
15 Configuration configuration = new Configuration();
16 configuration.configure("hibernate.cfg.xml");
17 System.out.println("Hibernate Configuration loaded");
18
19 ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
20 System.out.println("Hibernate serviceRegistry created");
21
22 SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
23
24 return sessionFactory;
25 }
26 catch (Throwable ex) {
27 System.err.println("Initial SessionFactory creation failed." + ex);
28 ex.printStackTrace();
29 throw new ExceptionInInitializerError(ex);
30 }
31 }
32
33 public static SessionFactory getSessionFactory() {
34 if(sessionFactory == null) sessionFactory = buildSessionFactory();
35 return sessionFactory;
36 }
37}
Hibernate配置XML文件
我们的hibernate配置xml文件包含数据库信息和映射资源细节。hibernate.cfg.xml
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-configuration PUBLIC
3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
4 "https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
5<hibernate-configuration>
6 <session-factory>
7 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
8 <property name="hibernate.connection.password">pankaj123</property>
9 <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
10 <property name="hibernate.connection.username">pankaj</property>
11 <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
12
13 <property name="hibernate.current_session_context_class">thread</property>
14 <property name="hibernate.show_sql">true</property>
15
16 <mapping resource="cart.hbm.xml"/>
17 <mapping resource="items.hbm.xml"/>
18 </session-factory>
19</hibernate-configuration>
Hibernate一对多映射示例--XML配置
这是教程中最重要的部分,让我们来看看如何在Hibernate中映射Cart和Items类进行一对多映射。cart.hbm.xml
1<?xml version="1.0"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3 "https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
4
5<hibernate-mapping package="com.journaldev.hibernate.model">
6 <class name="Cart" table="CART" >
7 <id name="id" type="long">
8 <column name="cart_id" />
9 <generator class="identity" />
10 </id>
11 <property name="total" type="double">
12 <column name="total" />
13 </property>
14 <property name="name" type="string">
15 <column name="name" />
16 </property>
17 <set name="items" table="ITEMS" fetch="select">
18 <key>
19 <column name="cart_id" not-null="true"></column>
20 </key>
21 <one-to-many class="Items"/>
22 </set>
23 </class>
24
25</hibernate-mapping>
重要的部分是其中的set
元素和one-to-many
元素。请注意,我们提供了用于一对多映射的键,即cart_id.items.hbm.xml
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
4
5<hibernate-mapping package="com.journaldev.hibernate.model">
6
7 <class name="Items" table="ITEMS">
8 <id name="id" type="long">
9 <column name="id" />
10 <generator class="identity" />
11 </id>
12 <property name="itemId" type="string">
13 <column name="item_id"></column>
14 </property>
15 <property name="itemTotal" type="double">
16 <column name="item_total"></column>
17 </property>
18 <property name="quantity" type="integer">
19 <column name="quantity"></column>
20 </property>
21
22 <many-to-one name="cart" class="Cart">
23 <column name="cart_id" not-null="true"></column>
24 </many-to-one>
25 </class>
26
27</hibernate-mapping>
注意,从商品到购物车,这是一种多对一的关系。所以我们需要对购物车使用`多对一‘元素,并且我们提供了将与键映射的列名。因此,基于购物车Hibernate映射配置,它的密钥cart_id将用于映射。我们的使用XML映射的Hibernate一对多映射示例的项目已经准备好了,让我们编写一个测试程序并检查它是否工作得很好。
Hibernate一对多映射示例-测试程序
HibernateOneToManyMain.java
1package com.journaldev.hibernate.main;
2
3import java.util.HashSet;
4import java.util.Set;
5
6import org.hibernate.Session;
7import org.hibernate.SessionFactory;
8import org.hibernate.Transaction;
9
10import com.journaldev.hibernate.model.Cart;
11import com.journaldev.hibernate.model.Items;
12import com.journaldev.hibernate.util.HibernateUtil;
13
14public class HibernateOneToManyMain {
15
16 public static void main(String[] args) {
17
18 Cart cart = new Cart();
19 cart.setName("MyCart");
20
21 Items item1 = new Items("I1", 10, 1, cart);
22 Items item2 = new Items("I2", 20, 2, cart);
23 Set<Items> itemsSet = new HashSet<Items>();
24 itemsSet.add(item1); itemsSet.add(item2);
25
26 cart.setItems(itemsSet);
27 cart.setTotal(10*1 + 20*2);
28
29 SessionFactory sessionFactory = null;
30 Session session = null;
31 Transaction tx = null;
32 try{
33 //Get Session
34 sessionFactory = HibernateUtil.getSessionFactory();
35 session = sessionFactory.getCurrentSession();
36 System.out.println("Session created");
37 //start transaction
38 tx = session.beginTransaction();
39
40 //Save the Model objects
41 session.save(cart);
42 session.save(item1);
43 session.save(item2);
44
45 //Commit transaction
46 tx.commit();
47 System.out.println("Cart ID="+cart.getId());
48
49 }catch(Exception e){
50 System.out.println("Exception occured. "+e.getMessage());
51 e.printStackTrace();
52 }finally{
53 if(!sessionFactory.isClosed()){
54 System.out.println("Closing SessionFactory");
55 sessionFactory.close();
56 }
57 }
58 }
59
60}
请注意,我们需要逐个保存Cart和Items对象。Hibernate将负责更新Items表中的外键。当我们执行上面的程序时,我们得到以下输出。
1Hibernate Configuration loaded
2Hibernate serviceRegistry created
3Session created
4Hibernate: insert into CART (total, name) values (?, ?)
5Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
6Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
7Hibernate: update ITEMS set cart_id=? where id=?
8Hibernate: update ITEMS set cart_id=? where id=?
9Cart ID=6
10Closing SessionFactory
请注意,Hibernate正在使用Update查询来设置ITEMS表中的cart_id。
Hibernate一对多映射注释
既然我们已经了解了如何使用基于XML的配置在Hibernate中实现一对多映射,让我们来看看如何使用JPA注释来做同样的事情。
Hibernate一对多映射示例注释
Hibernate配置文件几乎是相同的,只是映射元素发生了变化,因为我们使用类来进行一对多映射(使用注释)。休眠-注解.cfg.xml
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE hibernate-configuration PUBLIC
3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
4 "https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
5<hibernate-configuration>
6 <session-factory>
7 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
8 <property name="hibernate.connection.password">pankaj123</property>
9 <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
10 <property name="hibernate.connection.username">pankaj</property>
11 <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
12
13 <property name="hibernate.current_session_context_class">thread</property>
14 <property name="hibernate.show_sql">true</property>
15
16 <mapping class="com.journaldev.hibernate.model.Cart1"/>
17 <mapping class="com.journaldev.hibernate.model.Items1"/>
18 </session-factory>
19</hibernate-configuration>
Hibernate SessionFactory实用程序类
SessionFactory实用类几乎一样,我们只需要使用新的hibernate配置文件。HibernateAnnotationUtil.java
1package com.journaldev.hibernate.util;
2
3import org.hibernate.SessionFactory;
4import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
5import org.hibernate.cfg.Configuration;
6import org.hibernate.service.ServiceRegistry;
7
8public class HibernateAnnotationUtil {
9
10 private static SessionFactory sessionFactory;
11
12 private static SessionFactory buildSessionFactory() {
13 try {
14 // Create the SessionFactory from hibernate-annotation.cfg.xml
15 Configuration configuration = new Configuration();
16 configuration.configure("hibernate-annotation.cfg.xml");
17 System.out.println("Hibernate Annotation Configuration loaded");
18
19 ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
20 System.out.println("Hibernate Annotation serviceRegistry created");
21
22 SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
23
24 return sessionFactory;
25 }
26 catch (Throwable ex) {
27 System.err.println("Initial SessionFactory creation failed." + ex);
28 ex.printStackTrace();
29 throw new ExceptionInInitializerError(ex);
30 }
31 }
32
33 public static SessionFactory getSessionFactory() {
34 if(sessionFactory == null) sessionFactory = buildSessionFactory();
35 return sessionFactory;
36 }
37}
Hibernate一对多映射注释模型类
因为我们没有基于XML的映射文件,所以所有与映射相关的配置都将使用模型类中的JPA注释来完成。如果您理解基于XML的映射,它是非常简单和相似的。Cart1.java
1package com.journaldev.hibernate.model;
2
3import java.util.Set;
4
5import javax.persistence.Column;
6import javax.persistence.Entity;
7import javax.persistence.GeneratedValue;
8import javax.persistence.GenerationType;
9import javax.persistence.Id;
10import javax.persistence.OneToMany;
11import javax.persistence.Table;
12
13@Entity
14@Table(name="CART")
15public class Cart1 {
16
17 @Id
18 @GeneratedValue(strategy=GenerationType.IDENTITY)
19 @Column(name="cart_id")
20 private long id;
21
22 @Column(name="total")
23 private double total;
24
25 @Column(name="name")
26 private String name;
27
28 @OneToMany(mappedBy="cart1")
29 private Set<Items1> items1;
30
31// Getter Setter methods for properties
32}
需要注意的重要一点是OneToMany
注释,其中mappedBy
变量用于定义Items 1
类中用于映射目的的属性。所以我们应该在Items 1类中有一个名为cart 1
的属性。不要忘记包含所有的getter-setter方法。项目1.java
1package com.journaldev.hibernate.model;
2
3import javax.persistence.Column;
4import javax.persistence.Entity;
5import javax.persistence.GeneratedValue;
6import javax.persistence.GenerationType;
7import javax.persistence.Id;
8import javax.persistence.JoinColumn;
9import javax.persistence.ManyToOne;
10import javax.persistence.Table;
11
12@Entity
13@Table(name="ITEMS")
14public class Items1 {
15
16 @Id
17 @GeneratedValue(strategy=GenerationType.IDENTITY)
18 @Column(name="id")
19 private long id;
20
21 @Column(name="item_id")
22 private String itemId;
23
24 @Column(name="item_total")
25 private double itemTotal;
26
27 @Column(name="quantity")
28 private int quantity;
29
30 @ManyToOne
31 @JoinColumn(name="cart_id", nullable=false)
32 private Cart1 cart1;
33
34 //Hibernate requires no-args constructor
35 public Items1(){}
36
37 public Items1(String itemId, double total, int qty, Cart1 c){
38 this.itemId=itemId;
39 this.itemTotal=total;
40 this.quantity=qty;
41 this.cart1=c;
42 }
43//Getter Setter methods
44}
上面类中最重要的一点是Cart1类变量上的ManyToOne
注释和提供映射列名的JoinColumn
注释。在Hibernate中使用模型类中的注释的一对多映射就是这样。将其与基于XML的配置进行比较,您会发现它们非常相似。让我们编写一个测试程序并执行它。
Hibernate一对多映射注释示例测试程序
我们的测试程序就像基于XML的配置一样,我们只是使用新的类来获取Hibernate会话并将模型对象保存到数据库中。HibernateOneToManyAnnotationMain.java
1package com.journaldev.hibernate.main;
2
3import java.util.HashSet;
4import java.util.Set;
5
6import org.hibernate.Session;
7import org.hibernate.SessionFactory;
8import org.hibernate.Transaction;
9
10import com.journaldev.hibernate.model.Cart1;
11import com.journaldev.hibernate.model.Items1;
12import com.journaldev.hibernate.util.HibernateAnnotationUtil;
13
14public class HibernateOneToManyAnnotationMain {
15
16 public static void main(String[] args) {
17
18 Cart1 cart = new Cart1();
19 cart.setName("MyCart1");
20
21 Items1 item1 = new Items1("I10", 10, 1, cart);
22 Items1 item2 = new Items1("I20", 20, 2, cart);
23 Set<Items1> itemsSet = new HashSet<Items1>();
24 itemsSet.add(item1); itemsSet.add(item2);
25
26 cart.setItems1(itemsSet);
27 cart.setTotal(10*1 + 20*2);
28
29 SessionFactory sessionFactory = null;
30 Session session = null;
31 Transaction tx = null;
32 try{
33 //Get Session
34 sessionFactory = HibernateAnnotationUtil.getSessionFactory();
35 session = sessionFactory.getCurrentSession();
36 System.out.println("Session created");
37 //start transaction
38 tx = session.beginTransaction();
39 //Save the Model object
40 session.save(cart);
41 session.save(item1);
42 session.save(item2);
43 //Commit transaction
44 tx.commit();
45 System.out.println("Cart1 ID="+cart.getId());
46 System.out.println("item1 ID="+item1.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
47 System.out.println("item2 ID="+item2.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
48
49 }catch(Exception e){
50 System.out.println("Exception occured. "+e.getMessage());
51 e.printStackTrace();
52 }finally{
53 if(!sessionFactory.isClosed()){
54 System.out.println("Closing SessionFactory");
55 sessionFactory.close();
56 }
57 }
58 }
59
60}
当我们执行上面的Hibernate一对多映射注释示例测试程序时,我们得到了以下输出。
1Hibernate Annotation Configuration loaded
2Hibernate Annotation serviceRegistry created
3Session created
4Hibernate: insert into CART (name, total) values (?, ?)
5Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
6Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
7Cart1 ID=7
8item1 ID=9, Foreign Key Cart ID=7
9item2 ID=10, Foreign Key Cart ID=7
10Closing SessionFactory
这就是Hibernate一对多映射,从下面的链接下载样例项目,并做一些更多的实验。