Hibernate 一对多映射示例注释
今天,我们将研究Hibernate中的一对多映射。
我们将研究使用注释和XML配置的Hibernate一对多映射示例。
Hibernate 中的一对多映射
简单来说,一对多映射意味着表中的一行可以映射到另一表中的多行。
例如,考虑一个Cart系统,其中有另一个Items表。
一个购物车可以有多个物品,因此这里我们有一对多的映射。
我们将在Hibernate 的一对多映射示例中使用Cart-Items场景。
Hibernate中的一对多映射–数据库设置
我们可以将外键约束用于一对多映射。
以下是我们的"购物车"和"物品"表的数据库脚本。
我正在将MySQL数据库用于Hibernate一对多映射示例。
setup.sql
CREATE TABLE `Cart` ( `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT, `total` decimal(10,0) NOT NULL, `name` varchar(10) DEFAULT NULL, PRIMARY KEY (`cart_id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; CREATE TABLE `Items` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `cart_id` int(11) unsigned NOT NULL, `item_id` varchar(10) NOT NULL, `item_total` decimal(10,0) NOT NULL, `quantity` int(3) NOT NULL, PRIMARY KEY (`id`), KEY `cart_id` (`cart_id`), CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`) ) 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日志记录,它会自动作为传递依赖项添加。
<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.theitroad.hibernate</groupId> <artifactId>HibernateOneToManyMapping</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版本4.3.5.Final和MySQL驱动程序版本基于我的数据库安装。
Hibernate 一对多映射模型类
对于我们的表Cart和Items,我们有模型类来反映它们。
Cart.java
package com.theitroad.hibernate.model; import java.util.Set; public class Cart { private long id; private double total; private String name; private Set<Items> items; public long getId() { return id; } public void setId(long id) { this.id = id; } public double getTotal() { return total; } public void setTotal(double total) { this.total = total; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Items> getItems() { return items; } public void setItems(Set<Items> items) { this.items = items; } }
我正在使用项目集,以便每个记录都是唯一的。
我们也可以在Hibernate 中使用List或者Array进行一对多映射。
Items.java
package com.theitroad.hibernate.model; public class Items { private long id; private String itemId; private double itemTotal; private int quantity; private Cart cart; //Hibernate requires no-args constructor public Items(){} public Items(String itemId, double total, int qty, Cart c){ this.itemId=itemId; this.itemTotal=total; this.quantity=qty; this.cart=c; } public String getItemId() { return itemId; } public void setItemId(String itemId) { this.itemId = itemId; } public double getItemTotal() { return itemTotal; } public void setItemTotal(double itemTotal) { this.itemTotal = itemTotal; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } public Cart getCart() { return cart; } public void setCart(Cart cart) { this.cart = cart; } public long getId() { return id; } public void setId(long id) { this.id = id; } }
物品与购物车有多对一的关系,因此我们不需要具有"购物车收藏"对象。
Hibernate SessionFactory实用程序类
我们有一个用于创建Hibernate SessionFactory的实用程序类。
HibernateUtil.java
package com.theitroad.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; } }
Hibernate 配置XML文件
我们的Hibernate 配置xml文件包含数据库信息和映射资源详细信息。
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">hyman123</property> <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property> <property name="hibernate.connection.username">hyman</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="items.hbm.xml" </session-factory> </hibernate-configuration>
Hibernate 一对多映射示例– XML配置
这是教程中最重要的部分,让我们看看如何在冬眠中同时映射Cart和Items类以进行一对多映射。
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.theitroad.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 name="total" </property> <property name="name" type="string"> <column name="name" </property> <set name="items" table="ITEMS" fetch="select"> <key> <column name="cart_id" not-null="true"></column> </key> <one-to-many class="Items" </set> </class> </hibernate-mapping>
重要的部分是其中的set
元素和"一对多"元素。
请注意,我们提供了用于一对多映射的密钥,即cart_id。
items.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.theitroad.hibernate.model"> <class name="Items" table="ITEMS"> <id name="id" type="long"> <column name="id" <generator class="identity" </id> <property name="itemId" type="string"> <column name="item_id"></column> </property> <property name="itemTotal" type="double"> <column name="item_total"></column> </property> <property name="quantity" type="integer"> <column name="quantity"></column> </property> <many-to-one name="cart" class="Cart"> <column name="cart_id" not-null="true"></column> </many-to-one> </class> </hibernate-mapping>
请注意,从物品到购物车,这是多对一的关系。
因此,我们需要在购物车中使用"多对一"元素,并提供将与键映射的列名。
因此,根据购物车Hibernate 映射配置,密钥key_id将用于映射。
我们的使用XML映射的Hibernate一对多映射示例项目已经准备就绪,让我们编写一个测试程序,并检查其运行是否正常。
Hibernate 一对多映射示例–测试程序
HibernateOneToManyMain.java
package com.theitroad.hibernate.main; import java.util.HashSet; import java.util.Set; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import com.theitroad.hibernate.model.Cart; import com.theitroad.hibernate.model.Items; import com.theitroad.hibernate.util.HibernateUtil; public class HibernateOneToManyMain { public static void main(String[] args) { Cart cart = new Cart(); cart.setName("MyCart"); Items item1 = new Items("I1", 10, 1, cart); Items item2 = new Items("I2", 20, 2, cart); Set<Items> itemsSet = new HashSet<Items>(); itemsSet.add(item1); itemsSet.add(item2); cart.setItems(itemsSet); cart.setTotal(10*1 + 20*2); SessionFactory sessionFactory = null; Session session = null; Transaction tx = null; try{ //Get Session sessionFactory = HibernateUtil.getSessionFactory(); session = sessionFactory.getCurrentSession(); System.out.println("Session created"); //start transaction tx = session.beginTransaction(); //Save the Model objects session.save(cart); session.save(item1); session.save(item2); //Commit transaction tx.commit(); System.out.println("Cart ID="+cart.getId()); }catch(Exception e){ System.out.println("Exception occured. "+e.getMessage()); e.printStackTrace(); }finally{ if(!sessionFactory.isClosed()){ System.out.println("Closing SessionFactory"); sessionFactory.close(); } } } }
注意,我们需要一个一个地保存Cart和Items对象。
Hibernate将负责更新Items表中的外键。
当我们执行上面的程序时,我们得到以下输出。
Hibernate Configuration loaded Hibernate serviceRegistry created Session created Hibernate: insert into CART (total, name) values (?, ?) Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?) Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?) Hibernate: update ITEMS set cart_id=? where id=? Hibernate: update ITEMS set cart_id=? where id=? Cart ID=6 Closing SessionFactory
注意,Hibernate正在使用Update查询在ITEMS表中设置cart_id。
Hibernate 一对多映射注释
现在,我们已经了解了如何使用基于XML的配置在Hibernate中实现一对多映射,让我们来看看如何使用JPA批注执行相同的操作。
Hibernate 一对多映射示例注释
Hibernate配置文件几乎相同,不同之处在于映射元素发生了变化,因为我们使用Classes通过注释将Hibernate一对多映射到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">hyman123</property> <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property> <property name="hibernate.connection.username">hyman</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.theitroad.hibernate.model.Cart1" <mapping class="com.theitroad.hibernate.model.Items1" </session-factory> </hibernate-configuration>
Hibernate SessionFactory实用程序类
SessionFactory实用程序类几乎相同,我们只需要使用新的Hibernate 配置文件即可。
HibernateAnnotationUtil.java
package com.theitroad.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 一对多映射注释模型类
由于我们没有基于xml的映射文件,因此所有与映射相关的配置都将使用模型类中的JPA批注完成。
如果您了解基于xml的映射,那么它非常简单且相似。
Cart1.java
package com.theitroad.hibernate.model; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name="CART") public class Cart1 { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="cart_id") private long id; @Column(name="total") private double total; @Column(name="name") private String name; @OneToMany(mappedBy="cart1") private Set<Items1> items1; //Getter Setter methods for properties }
需要注意的重要一点是" OneToMany"注释,其中" mappedBy"变量用于定义" Items1"类中的属性,该属性将用于映射。
因此,我们应该在Items1类中有一个名为" cart1"的属性。
不要忘记包括所有的getter-setter方法。
Items1.java
package com.theitroad.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.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name="ITEMS") public class Items1 { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="id") private long id; @Column(name="item_id") private String itemId; @Column(name="item_total") private double itemTotal; @Column(name="quantity") private int quantity; @ManyToOne @JoinColumn(name="cart_id", nullable=false) private Cart1 cart1; //Hibernate requires no-args constructor public Items1(){} public Items1(String itemId, double total, int qty, Cart1 c){ this.itemId=itemId; this.itemTotal=total; this.quantity=qty; this.cart1=c; } //Getter Setter methods }
上一类中最重要的一点是在Cart1类变量上的ManyToOne注释和为映射提供列名称的JoinColumn注释。
在Hibernate 中使用模型类中的注释进行一对多映射就可以了。
将其与基于XML的配置进行比较,您会发现它们非常相似。
让我们编写一个测试程序并执行它。
Hibernate 一对多映射注释示例测试程序
我们的测试程序就像基于xml的配置一样,我们只是使用新类来获取Hibernate Session并将模型对象保存到数据库中。
HibernateOneToManyAnnotationMain.java
package com.theitroad.hibernate.main; import java.util.HashSet; import java.util.Set; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import com.theitroad.hibernate.model.Cart1; import com.theitroad.hibernate.model.Items1; import com.theitroad.hibernate.util.HibernateAnnotationUtil; public class HibernateOneToManyAnnotationMain { public static void main(String[] args) { Cart1 cart = new Cart1(); cart.setName("MyCart1"); Items1 item1 = new Items1("I10", 10, 1, cart); Items1 item2 = new Items1("I20", 20, 2, cart); Set<Items1> itemsSet = new HashSet<Items1>(); itemsSet.add(item1); itemsSet.add(item2); cart.setItems1(itemsSet); cart.setTotal(10*1 + 20*2); SessionFactory sessionFactory = null; Session session = null; Transaction tx = null; try{ //Get Session sessionFactory = HibernateAnnotationUtil.getSessionFactory(); session = sessionFactory.getCurrentSession(); System.out.println("Session created"); //start transaction tx = session.beginTransaction(); //Save the Model object session.save(cart); session.save(item1); session.save(item2); //Commit transaction tx.commit(); System.out.println("Cart1 ID="+cart.getId()); System.out.println("item1 ID="+item1.getId()+", Foreign Key Cart ID="+item1.getCart1().getId()); System.out.println("item2 ID="+item2.getId()+", Foreign Key Cart ID="+item1.getCart1().getId()); }catch(Exception e){ System.out.println("Exception occured. "+e.getMessage()); e.printStackTrace(); }finally{ if(!sessionFactory.isClosed()){ System.out.println("Closing SessionFactory"); sessionFactory.close(); } } } }
当我们在Hibernate 状态下执行一对多映射注释示例测试程序时,将得到以下输出。
Hibernate Annotation Configuration loaded Hibernate Annotation serviceRegistry created Session created Hibernate: insert into CART (name, total) values (?, ?) Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?) Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?) Cart1 ID=7 item1 ID=9, Foreign Key Cart ID=7 item2 ID=10, Foreign Key Cart ID=7 Closing SessionFactory
这就是Hibernate 一对多映射,从下面的链接下载示例项目并进行更多实验的全部步骤。