- 背景:
在实际开发中我们会遇到表的多对多关联,比如:一篇博客文章,它可以同时属于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 @Before 25 public void init() { 26 StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder().configure().build(); 27 Metadata metadata = 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 @After 35 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=InnoDB 16 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=InnoDB 24 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 @Test 2 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 category1 11 Hibernate: 12 select 13 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 from 19 PRODUCT_CATEGORY_ITEM productite0_ 20 inner join 21 PRODUCT_ITEM productite1_ 22 on productite0_.ITEM_ID=productite1_.ID 23 where 24 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=InnoDB 16 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=InnoDB 24 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 into 39 PRODUCT_CATEGORY 40 (NAME, DETAIL) 41 values 42 (?, ?) 43 Hibernate: 44 insert 45 into 46 PRODUCT_CATEGORY 47 (NAME, DETAIL) 48 values 49 (?, ?) 50 Hibernate: 51 insert 52 into 53 PRODUCT_ITEM 54 (TITLE, PRICE) 55 values 56 (?, ?) 57 Hibernate: 58 insert 59 into 60 PRODUCT_ITEM 61 (TITLE, PRICE) 62 values 63 (?, ?) 64 Hibernate: 65 insert 66 into 67 PRODUCT_CATEGORY_ITEM 68 (CATEGORY_ID, ITEM_ID) 69 values 70 (?, ?) 71 Hibernate: 72 insert 73 into 74 PRODUCT_CATEGORY_ITEM 75 (CATEGORY_ID, ITEM_ID) 76 values 77 (?, ?) 78 Hibernate: 79 insert 80 into 81 PRODUCT_CATEGORY_ITEM 82 (CATEGORY_ID, ITEM_ID) 83 values 84 (?, ?) 85 Hibernate: 86 insert 87 into 88 PRODUCT_CATEGORY_ITEM 89 (CATEGORY_ID, ITEM_ID) 90 values 91 (?, ?)
在数据中执行查询: