首页 > 代码库 > Hibernate(十):n-n关联关系
Hibernate(十):n-n关联关系
- 背景:
在实际开发中我们会遇到表的多对多关联,比如:一篇博客文章,它可以同时属于JAVA分类、Hibernate分类。
因此,我们在hibernate的学习文章系列中,需要学会如何使用hibernate来实现多对多的关联关系。
在hibernate实现多对多的关联关系中,也是需要创建一个中间表来存储、维护两张表的多对多的关系。具体实现有两种可选方案:单向多对多、双向多对多。
- 单向多对多:
新建一个java project,定义项目名称为:hibernate07;在src下添加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 "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> 5 <hibernate-configuration> 6 <session-factory> 7 <property name="hibernate.connection.username">root</property> 8 <property name="hibernate.connection.password">123456</property> 9 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>10 <property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate_01</property>11 12 <!-- <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 13 <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> -->14 <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>15 16 <property name="hibernate.show_sql">true</property>17 18 <property name="hibernate.format_sql">true</property>19 20 <property name="hibernate.hbm2ddl.auto">update</property>21 22 <property name="hibernate.current_session_context_class">thread</property>23 24 <property name="hibernate.c3p0.max_size">500</property>25 <property name="hibernate.c3p0.min_size">20</property>26 <property name="hibernate.c3p0.max_statements">10</property>27 <property name="hibernate.c3p0.timeout">2000</property>28 <property name="hibernate.c3p0.idle_test_period">2000</property>29 <property name="hibernate.c3p0.acquire_increment">10</property>30 31 <mapping resource="com/dx/hibernate06/n2n/ProductCategory.hbm.xml" /> 32 <mapping resource="com/dx/hibernate06/n2n/ProductItem.hbm.xml" />33 34 </session-factory>35 </hibernate-configuration>
在src下创建包com.dx.hibernate06.n2n,在包下创建:
ProductCategory.java(在category这个类中创建了一个Set<ProductItem> productItems 属性)
1 package com.dx.hibernate06.n2n; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class ProductCategory { 7 private Integer id; 8 private String name; 9 private String detail;10 private Set<ProductItem> productItems = new HashSet<>();11 12 public ProductCategory() {13 14 }15 16 public ProductCategory(String name, String detail) {17 super();18 this.name = name;19 this.detail = detail;20 }21 22 public Integer getId() {23 return id;24 }25 26 public void setId(Integer id) {27 this.id = id;28 }29 30 public String getName() {31 return name;32 }33 34 public void setName(String name) {35 this.name = name;36 }37 38 public String getDetail() {39 return detail;40 }41 42 public void setDetail(String detail) {43 this.detail = detail;44 }45 46 public Set<ProductItem> getProductItems() {47 return productItems;48 }49 50 public void setProductItems(Set<ProductItem> productItems) {51 this.productItems = productItems;52 }53 54 }
ProductCategory.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 4 <!-- Generated 2017-6-7 22:33:53 by Hibernate Tools 3.5.0.Final --> 5 <hibernate-mapping package="com.dx.hibernate06.n2n"> 6 <class name="ProductCategory" table="PRODUCT_CATEGORY"> 7 <id name="id" type="java.lang.Integer"> 8 <column name="ID" /> 9 <generator class="native" />10 </id>11 <property name="name" type="java.lang.String">12 <column name="NAME" />13 </property>14 <property name="detail" type="java.lang.String">15 <column name="DETAIL" />16 </property>17 18 <set name="productItems" table="PRODUCT_CATEGORY_ITEM">19 <key>20 <column name="CATEGORY_ID" />21 </key>22 <many-to-many class="ProductItem" column="ITEM_ID"></many-to-many>23 </set>24 </class>25 </hibernate-mapping>
备注:在ProductCategory.hbm.xml的set节点我们定义的table属性,并定义了many-to-many节点用来指向ProductItem。
ProductItem.java
1 package com.dx.hibernate06.n2n; 2 3 public class ProductItem { 4 private Integer id; 5 private String title; 6 private double price; 7 8 public ProductItem() { 9 }10 11 public ProductItem(String title, double price) {12 super();13 this.title = title;14 this.price = price;15 }16 17 public Integer getId() {18 return id;19 }20 21 public void setId(Integer id) {22 this.id = id;23 }24 25 public String getTitle() {26 return title;27 }28 29 public void setTitle(String title) {30 this.title = title;31 }32 33 public double getPrice() {34 return price;35 }36 37 public void setPrice(double price) {38 this.price = price;39 }40 41 }
ProductItem.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 4 <!-- Generated 2017-6-7 22:33:53 by Hibernate Tools 3.5.0.Final --> 5 <hibernate-mapping> 6 <class name="com.dx.hibernate06.n2n.ProductItem" table="PRODUCT_ITEM"> 7 <id name="id" type="java.lang.Integer"> 8 <column name="ID" /> 9 <generator class="native" />10 </id>11 <property name="title" type="java.lang.String">12 <column name="TITLE" />13 </property>14 <property name="price" type="double">15 <column name="PRICE" />16 </property>17 </class>18 </hibernate-mapping>
测试类TestMain.java
1 package com.dx.hibernate06.n2n; 2 3 import java.util.Date; 4 import java.util.Set; 5 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.hibernate.Transaction; 9 import org.hibernate.boot.Metadata;10 import org.hibernate.boot.MetadataSources;11 import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl;12 import org.hibernate.boot.registry.StandardServiceRegistry;13 import org.hibernate.boot.registry.StandardServiceRegistryBuilder;14 import org.hibernate.metamodel.internal.MapMember;15 import org.junit.After;16 import org.junit.Before;17 import org.junit.Test;18 19 public class TestMain {20 private SessionFactory sessionFactory = null;21 private Session session = null;22 private Transaction transaction = null;23 24 @Before25 public void init() {26 StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder().configure().build();27 Metadata metadata = http://www.mamicode.com/new MetadataSources(standardRegistry).getMetadataBuilder().applyImplicitNamingStrategy(ImplicitNamingStrategyComponentPathImpl.INSTANCE).build();28 29 sessionFactory = metadata.getSessionFactoryBuilder().build();30 session = sessionFactory.getCurrentSession();31 transaction = session.beginTransaction();32 }33 34 @After35 public void destory() {36 transaction.commit();37 session.close();38 sessionFactory.close();39 }40 }
测试代码:
添加测试函数1:
1 @Test 2 public void testInsert() { 3 ProductCategory category1 = new ProductCategory(); 4 category1.setName("category1"); 5 category1.setDetail("Detail"); 6 7 ProductCategory category2 = new ProductCategory(); 8 category2.setName("category2"); 9 category2.setDetail("Detail");10 11 ProductItem item1 = new ProductItem();12 item1.setTitle("item1");13 item1.setPrice(110.00);14 15 ProductItem item2 = new ProductItem();16 item2.setTitle("item2");17 item2.setPrice(110.00);18 19 category1.getProductItems().add(item1);20 category1.getProductItems().add(item2);21 22 category2.getProductItems().add(item1);23 category2.getProductItems().add(item2);24 25 session.save(category1);26 session.save(category2);27 28 session.save(item1);29 session.save(item2);30 }
控制台打印sql
1 Hibernate: 2 3 create table PRODUCT_CATEGORY ( 4 ID integer not null auto_increment, 5 NAME varchar(255), 6 DETAIL varchar(255), 7 primary key (ID) 8 ) engine=InnoDB 9 Hibernate: 10 11 create table PRODUCT_CATEGORY_ITEM (12 CATEGORY_ID integer not null,13 ITEM_ID integer not null,14 primary key (CATEGORY_ID, ITEM_ID)15 ) engine=InnoDB16 Hibernate: 17 18 create table PRODUCT_ITEM (19 ID integer not null auto_increment,20 TITLE varchar(255),21 PRICE double precision,22 primary key (ID)23 ) engine=InnoDB24 Hibernate: 25 26 alter table PRODUCT_CATEGORY_ITEM 27 add constraint FKgqq9f2yg5b52m390yk15c8u28 28 foreign key (ITEM_ID) 29 references PRODUCT_ITEM (ID)30 Hibernate: 31 32 alter table PRODUCT_CATEGORY_ITEM 33 add constraint FKtajc52s55t4fk8864s63hsuv2 34 foreign key (CATEGORY_ID) 35 references PRODUCT_CATEGORY (ID)
查询数据库结果信息:
添加测试函数2:
1 @Test2 public void testSelect() {3 ProductCategory category = (ProductCategory) session.get(ProductCategory.class, 1);4 System.out.println(category.getName());5 6 System.out.println(category.getProductItems().size());7 }
后台执行sql及结果:
1 Hibernate: 2 select 3 productcat0_.ID as ID1_0_0_, 4 productcat0_.NAME as NAME2_0_0_, 5 productcat0_.DETAIL as DETAIL3_0_0_ 6 from 7 PRODUCT_CATEGORY productcat0_ 8 where 9 productcat0_.ID=?10 category111 Hibernate: 12 select13 productite0_.CATEGORY_ID as CATEGORY1_1_0_,14 productite0_.ITEM_ID as ITEM_ID2_1_0_,15 productite1_.ID as ID1_2_1_,16 productite1_.TITLE as TITLE2_2_1_,17 productite1_.PRICE as PRICE3_2_1_ 18 from19 PRODUCT_CATEGORY_ITEM productite0_ 20 inner join21 PRODUCT_ITEM productite1_ 22 on productite0_.ITEM_ID=productite1_.ID 23 where24 productite0_.CATEGORY_ID=?25 2
- 双向多对多:
实现双向多对多,需要再ProductItem的另一端也定义Set属性:Set<ProductCategory> productCategories。还需要在ProductItem.hbm.xml中添加set节点,节点属性配置与ProductCategory.hbm.xml中set节点配置对调。
修改ProductItem.java(在类中添加属性:Set<ProductCategory> productCategories):
1 package com.dx.hibernate06.n2n; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class ProductItem { 7 private Integer id; 8 private String title; 9 private double price;10 private Set<ProductCategory> productCategories = new HashSet<>();11 12 public ProductItem() {13 }14 15 public ProductItem(String title, double price) {16 super();17 this.title = title;18 this.price = price;19 }20 21 public Integer getId() {22 return id;23 }24 25 public void setId(Integer id) {26 this.id = id;27 }28 29 public String getTitle() {30 return title;31 }32 33 public void setTitle(String title) {34 this.title = title;35 }36 37 public double getPrice() {38 return price;39 }40 41 public void setPrice(double price) {42 this.price = price;43 }44 45 public Set<ProductCategory> getProductCategories() {46 return productCategories;47 }48 49 public void setProductCategories(Set<ProductCategory> productCategories) {50 this.productCategories = productCategories;51 }52 53 }
修改ProductItem.hbm.xml配置文件(添加set节点,并在ProductItem.hbm.xml或者ProductCategory.hbm.xml的set节点中添加属性inverse="true"):
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 4 <!-- Generated 2017-6-7 22:33:53 by Hibernate Tools 3.5.0.Final --> 5 <hibernate-mapping> 6 <class name="com.dx.hibernate06.n2n.ProductItem" table="PRODUCT_ITEM"> 7 <id name="id" type="java.lang.Integer"> 8 <column name="ID" /> 9 <generator class="native" />10 </id>11 <property name="title" type="java.lang.String">12 <column name="TITLE" />13 </property>14 <property name="price" type="double">15 <column name="PRICE" />16 </property>17 18 <set name="productCategories" table="PRODUCT_CATEGORY_ITEM" inverse="true">19 <key>20 <column name="ITEM_ID" />21 </key>22 <many-to-many class="com.dx.hibernate06.n2n.ProductCategory" column="CATEGORY_ID"></many-to-many>23 </set>24 </class>25 </hibernate-mapping>
删除数据中的表,之后添加测试函数。
mysql> drop table PRODUCT_CATEGORY_ITEM;Query OK, 0 rows affected (0.02 sec)mysql> drop table PRODUCT_CATEGORY;Query OK, 0 rows affected (0.02 sec)mysql> drop table PRODUCT_ITEM;Query OK, 0 rows affected (0.01 sec)mysql> show tables;+------------------------+| Tables_in_hibernate_01 |+------------------------+| customer || deparments || managers || member || memberdetail || news || orders |+------------------------+7 rows in set (0.00 sec)mysql>
测试代码:
添加测试函数1:
1 @Test 2 public void testInsert() { 3 ProductCategory category1 = new ProductCategory(); 4 category1.setName("category1"); 5 category1.setDetail("Detail"); 6 7 ProductCategory category2 = new ProductCategory(); 8 category2.setName("category2"); 9 category2.setDetail("Detail");10 11 ProductItem item1 = new ProductItem();12 item1.setTitle("item1");13 item1.setPrice(110.00);14 15 ProductItem item2 = new ProductItem();16 item2.setTitle("item2");17 item2.setPrice(110.00);18 19 category1.getProductItems().add(item1);20 category1.getProductItems().add(item2);21 category2.getProductItems().add(item1);22 category2.getProductItems().add(item2);23 24 item1.getProductCategories().add(category1);25 item1.getProductCategories().add(category2);26 item2.getProductCategories().add(category1);27 item2.getProductCategories().add(category2);28 29 session.save(category1);30 session.save(category2);31 32 session.save(item1);33 session.save(item2);34 }
测试执行sql:
1 Hibernate: 2 3 create table PRODUCT_CATEGORY ( 4 ID integer not null auto_increment, 5 NAME varchar(255), 6 DETAIL varchar(255), 7 primary key (ID) 8 ) engine=InnoDB 9 Hibernate: 10 11 create table PRODUCT_CATEGORY_ITEM (12 CATEGORY_ID integer not null,13 ITEM_ID integer not null,14 primary key (CATEGORY_ID, ITEM_ID)15 ) engine=InnoDB16 Hibernate: 17 18 create table PRODUCT_ITEM (19 ID integer not null auto_increment,20 TITLE varchar(255),21 PRICE double precision,22 primary key (ID)23 ) engine=InnoDB24 Hibernate: 25 26 alter table PRODUCT_CATEGORY_ITEM 27 add constraint FKgqq9f2yg5b52m390yk15c8u28 28 foreign key (ITEM_ID) 29 references PRODUCT_ITEM (ID)30 Hibernate: 31 32 alter table PRODUCT_CATEGORY_ITEM 33 add constraint FKtajc52s55t4fk8864s63hsuv2 34 foreign key (CATEGORY_ID) 35 references PRODUCT_CATEGORY (ID)36 Hibernate: 37 insert 38 into39 PRODUCT_CATEGORY40 (NAME, DETAIL) 41 values42 (?, ?)43 Hibernate: 44 insert 45 into46 PRODUCT_CATEGORY47 (NAME, DETAIL) 48 values49 (?, ?)50 Hibernate: 51 insert 52 into53 PRODUCT_ITEM54 (TITLE, PRICE) 55 values56 (?, ?)57 Hibernate: 58 insert 59 into60 PRODUCT_ITEM61 (TITLE, PRICE) 62 values63 (?, ?)64 Hibernate: 65 insert 66 into67 PRODUCT_CATEGORY_ITEM68 (CATEGORY_ID, ITEM_ID) 69 values70 (?, ?)71 Hibernate: 72 insert 73 into74 PRODUCT_CATEGORY_ITEM75 (CATEGORY_ID, ITEM_ID) 76 values77 (?, ?)78 Hibernate: 79 insert 80 into81 PRODUCT_CATEGORY_ITEM82 (CATEGORY_ID, ITEM_ID) 83 values84 (?, ?)85 Hibernate: 86 insert 87 into88 PRODUCT_CATEGORY_ITEM89 (CATEGORY_ID, ITEM_ID) 90 values91 (?, ?)
在数据中执行查询:
Hibernate(十):n-n关联关系