Hibernate表关系映射之多对多映射

矫情吗;* 2022-09-28 11:38 298阅读 0赞

一、多对多的实现原理

在数据库中实现多对多的关系,必须使用连接表。也就是用一个独立的表来存入两个表的主键字段,通过遍历这张表来获取两表的关联关系。

而在我们的对象中,多对多是通过两者对象类中互相建立对方类的对象集合,类似上一篇讲到的一对多的集合类!

在Hibernate中是通过来进行设置。同样多对多也存在双向和单向之分,表现到Hibernate中就是配置文件和POJO代码的不同。这里以单向映射为例,双向映射与之类似。

二、实例演示

1、建立数据模型

我们以学生与老师关系为例。一个学生可以有多个老师,而一个老师也一样可以有多名学生!这里我们让老师Teacher持有学生Student的集合引用。

Teacher.java

  1. package com.chen.many2many;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. public class Teacher {
  5. private Integer teacherID;
  6. private String teacherName;
  7. //持有一个student的Set集合
  8. private Set<Student> studentSet=new HashSet<>();
  9. public Integer getTeacherID() {
  10. return teacherID;
  11. }
  12. public void setTeacherID(Integer teacherID) {
  13. this.teacherID = teacherID;
  14. }
  15. public String getTeacherName() {
  16. return teacherName;
  17. }
  18. public void setTeacherName(String teacherName) {
  19. this.teacherName = teacherName;
  20. }
  21. public Set<Student> getStudentSet() {
  22. return studentSet;
  23. }
  24. public void setStudentSet(Set<Student> studentSet) {
  25. this.studentSet = studentSet;
  26. }
  27. }

Student.java

  1. package com.chen.many2many;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. public class Student {
  5. private Integer studentID;
  6. private String studentName;
  7. //持有一个teacher的Set集合
  8. //private Set<Teacher> teacherSet=new HashSet<>();
  9. public Integer getStudentID() {
  10. return studentID;
  11. }
  12. public void setStudentID(Integer studentID) {
  13. this.studentID = studentID;
  14. }
  15. public String getStudentName() {
  16. return studentName;
  17. }
  18. public void setStudentName(String studentName) {
  19. this.studentName = studentName;
  20. }
  21. /* public Set<Teacher> getTeacherSet() {
  22. return teacherSet;
  23. }
  24. public void setTeacherSet(Set<Teacher> teacherSet) {
  25. this.teacherSet = teacherSet;
  26. }
  27. */
  28. }

2、Hibernate配置文件

Student.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-3-10 14:43:39 by Hibernate Tools 3.4.0.CR1 -->
  5. <hibernate-mapping>
  6. <class name="com.chen.many2many.Student" table="STUDENTS">
  7. <id name="studentID" type="java.lang.Integer">
  8. <column name="STUDENT_ID" />
  9. <generator class="native" />
  10. </id>
  11. <property name="studentName" type="java.lang.String">
  12. <column name="STUDENT_NAME" />
  13. </property>
  14. <!-- 指定双向映射时需要加入如下设置
  15. <set name="teacherSet" table="TEACHERS_STUDENTS" inverse="true">
  16. <key>
  17. <column name="S_ID" />
  18. </key>
  19. <many-to-many class="com.chen.many2many.Teacher" column="T_ID"/>
  20. </set> -->
  21. </class>
  22. </hibernate-mapping>

Teacher.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-3-10 14:43:39 by Hibernate Tools 3.4.0.CR1 -->
  5. <hibernate-mapping>
  6. <class name="com.chen.many2many.Teacher" table="TEACHERS">
  7. <id name="teacherID" type="java.lang.Integer">
  8. <column name="TEACHER_ID" />
  9. <generator class="native" />
  10. </id>
  11. <property name="teacherName" type="java.lang.String">
  12. <column name="TEACHER_NAME" />
  13. </property>
  14. <!-- name是该类(Teacher)中持有的另一端的集合属性名 ,table指定了中间表的名字-->
  15. <set name="studentSet" table="TEACHERS_STUDENTS">
  16. <key>
  17. <!-- 当前类(Teacher)映射到中间表上时,使用的外键名称 -->
  18. <column name="T_ID" />
  19. </key>
  20. <!-- many-to-many完成多对多映射,class指定另一端(Student)的类名。
  21. column指定另一端(Student)在中间表中对应的外键名 -->
  22. <many-to-many class="com.chen.many2many.Student" column="S_ID" />
  23. </set>
  24. </class>
  25. </hibernate-mapping>

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://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6. <session-factory>
  7. <!-- Hibernate 连接数据库的基本信息 -->
  8. <property name="connection.username">root</property>
  9. <property name="connection.password">root</property>
  10. <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
  11. <property name="connection.url">jdbc:mysql://192.168.1.148:3306/hibernate5</property>
  12. <!-- Hibernate 的基本配置 -->
  13. <!-- Hibernate 使用的数据库方言,为了使mysql自动生成数据表
  14. 对于mysql5.x使用如下设置 -->
  15. <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
  16. <!-- 运行时是否打印 SQL -->
  17. <property name="show_sql">true</property>
  18. <!-- 运行时是否格式化 SQL -->
  19. <property name="format_sql">true</property>
  20. <!-- 生成数据表的策略 -->
  21. <property name="hbm2ddl.auto">update</property>
  22. <!-- 设置 Hibernate 的事务隔离级别 -->
  23. <property name="connection.isolation">2</property>
  24. <!-- 删除对象后, 使其 OID 置为 null -->
  25. <property name="use_identifier_rollback">true</property>
  26. <!-- 配置 C3P0 数据源 -->
  27. <property name="hibernate.c3p0.max_size">10</property>
  28. <property name="hibernate.c3p0.min_size">5</property>
  29. <property name="c3p0.acquire_increment">2</property>
  30. <property name="c3p0.idle_test_period">2000</property>
  31. <property name="c3p0.timeout">2000</property>
  32. <property name="c3p0.max_statements">10</property>
  33. <!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
  34. <property name="hibernate.jdbc.fetch_size">100</property>
  35. <!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->
  36. <property name="jdbc.batch_size">30</property>
  37. <!-- 需要关联的 hibernate 映射文件 .hbm.xml -->
  38. <mapping resource="com/chen/many2many/Student.hbm.xml"/>
  39. <mapping resource="com/chen/many2many/Teacher.hbm.xml"/>
  40. </session-factory>
  41. </hibernate-configuration>

3、编写测试方法

HibernateTest.java

  1. package com.chen.many2many;
  2. import java.util.Iterator;
  3. import java.util.Set;
  4. import org.hibernate.Session;
  5. import org.hibernate.SessionFactory;
  6. import org.hibernate.Transaction;
  7. import org.hibernate.cfg.Configuration;
  8. import org.hibernate.service.ServiceRegistry;
  9. import org.hibernate.service.ServiceRegistryBuilder;
  10. public class HibernateTest {
  11. private static SessionFactory sessionFactory;
  12. private static Session session;
  13. private static Transaction transaction;
  14. //定义事务初始化函数
  15. public static void init(){
  16. Configuration configuration = new Configuration().configure();
  17. ServiceRegistry serviceRegistry =
  18. new ServiceRegistryBuilder().applySettings(configuration.getProperties())
  19. .buildServiceRegistry();
  20. sessionFactory = configuration.buildSessionFactory(serviceRegistry);
  21. session = sessionFactory.openSession();
  22. transaction = session.beginTransaction();
  23. }
  24. //定义事务结束时的处理函数
  25. public static void doit(){
  26. transaction.commit();
  27. session.close();
  28. sessionFactory.close();
  29. }
  30. //编写自己的处理过程
  31. public static void testSave(){
  32. //学生: a,b,c
  33. //教师:A,B
  34. //所属关系A(a,b) B(a,b,c)
  35. //初始化学生
  36. Student a=new Student();
  37. a.setStudentName("a");
  38. Student b = new Student();
  39. b.setStudentName("b");
  40. Student c=new Student();
  41. c.setStudentName("c");
  42. //初始化老师
  43. Teacher A=new Teacher();
  44. A.setTeacherName("A");
  45. Teacher B=new Teacher();
  46. B.setTeacherName("B");
  47. //设置关联关系
  48. A.getStudentSet().add(a);
  49. A.getStudentSet().add(b);
  50. B.getStudentSet().add(a);
  51. B.getStudentSet().add(b);
  52. B.getStudentSet().add(c);
  53. //保存
  54. session.save(a);
  55. session.save(b);
  56. session.save(c);
  57. session.save(A);
  58. session.save(B);
  59. }
  60. public static void testGet(){
  61. Teacher teacherResult=(Teacher) session.get(Teacher.class, 1);
  62. System.out.println(teacherResult.getTeacherName());
  63. Set<Student> set=teacherResult.getStudentSet();
  64. for (Iterator iterator = set.iterator(); iterator.hasNext();) {
  65. Student student = (Student) iterator.next();
  66. System.out.println(student.getStudentName());
  67. }
  68. }
  69. public static void main(String[] args) {
  70. HibernateTest.init();
  71. HibernateTest.testSave();
  72. HibernateTest.testGet();
  73. HibernateTest.doit();
  74. System.out.println("完成---------");
  75. }
  76. }

4、运行结果

数据表students

20170310155501506

数据表teachers

20170310155546523

中间表teachers_students

20170310155639633

控制台显示结果:

20170310155741403

三、多对多双向映射注意项

1、双向 n-n 关联需要两端都持有对方的一个集合对象引用。
2、双向n-n关联必须使用连接表。
3、集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类。
4、在双向 n-n 关联的两边,都需指定连接表的表名及外键列的列名.。两个集合元素 set 的 table 元素的值必须指定,而且必须相同,这样就指定了同一张连接表。

5、set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名,因此两边的 key 与 many-to-many 的column属性交叉相同。也就是说,一边的set元素的key的 cloumn值为a,many-to-many 的 column 为b;则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a。
6、对于双向 n-n 关联, 必须把其中一端的 inverse 设置为 true, 否则两端都维护关联关系可能会造成主键冲突。

发表评论

表情:
评论列表 (有 0 条评论,298人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Hibernate关系映射之一映射

    一、基本概述 在表中的一对多,是使用外键关联,通过一张表的一个键另一个表的外键来建立一多关系;而在类中表示为一个类中有一个集合属性包含对方类的很多对象,而在另一个类中,只包