多对多(many-to-many):在操作和性能方面都不太理想,所以多对多的映射使用较少,实际使用中最好转换成一对多的对象模型;hibernate会为我们创建中间关联表,转换成两个一对多。

1. E-R图

2. 实体类:

Teacher实体类如下:

  1. package com.reiyen.hibernate.domain;
  2. import java.util.Set;
  3. public class Teacher {
  4. private int id;
  5. private String name;
  6. private Set<Student> students;
  7. //setter和getter方法
  8. }

Student实体类如下:

  1. package com.reiyen.hibernate.domain;
  2. import java.util.Set;
  3. public class Student {
  4. private int id;
  5. private String name;
  6. private Set<Teacher> teachers;
  7. //setter和getter方法
  8. }

3.映射文件如下:

Teacher.hbm.xml如下:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">
  6. <class name="Teacher">
  7. <id name="id">
  8. <generator class="native" />
  9. </id>
  10. <property name="name" />
  11. <!-- 通过table项告诉hibernate中间表的名称 -->
  12. <set name="students" table="teacher_student">
  13. <!-- 通过key属性告诉hibernate在中间表里面查询teacher_id值相应的teacher记录 -->
  14. <key column="teacher_id" />
  15. <!-- 通过column项告诉hibernate对student表中查找student_id值相就的studnet记录 -->
  16. <many-to-many class="Student" column="student_id" />
  17. </set>
  18. </class>
  19. </hibernate-mapping>

Student.hbm.xml如下:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">
  6. <class name="Student" >
  7. <id name="id" >
  8. <generator class="native" />
  9. </id>
  10. <property name="name" />
  11. <set name="teachers" table="teacher_student">
  12. <key column="student_id" />
  13. <many-to-many class="Teacher" column="teacher_id"/>
  14. </set>
  15. </class>
  16. </hibernate-mapping>

一定要注意映射文件中<many-to-many class="Teacher" column="teacher_id"/>中class的值,它必须与你另一个关联映射文件中的class属性的name值一致,其实就是与你的实体类的类名一致,如:<many-to-many class="Teacher" column="teacher_id"/>中class的值就不能写成"teacher"。如果写成这样的话,就会抛出如下异常:An association
from the table teacher_student refers to an unmapped class: com.reiyen .hibernate.domain.teacher

4. 测试程序如下:

  1. public class Many2Many {
  2. public static void main(String[] args) {
  3. add();
  4. //query(1);
  5. }
  6. static void query(int id) {
  7. Session s = null;
  8. Transaction tx = null;
  9. try {
  10. s = HibernateUtil.getSession();
  11. tx = s.beginTransaction();
  12. Teacher t = (Teacher) s.get(Teacher.class, id);
  13. System.out.println("students:" + t.getStudents().size());
  14. tx.commit();
  15. } finally {
  16. if (s != null)
  17. s.close();
  18. }
  19. }
  20. static void add() {
  21. Session s = null;
  22. Transaction tx = null;
  23. try {
  24. Set<Teacher> ts = new HashSet<Teacher>();
  25. Teacher t1 = new Teacher();
  26. t1.setName("t1 name");
  27. ts.add(t1);
  28. Teacher t2 = new Teacher();
  29. t2.setName("t2 name");
  30. ts.add(t2);
  31. Set<Student> ss = new HashSet<Student>();
  32. Student s1 = new Student();
  33. s1.setName("s1");
  34. ss.add(s1);
  35. Student s2 = new Student();
  36. s2.setName("s2");
  37. ss.add(s2);
  38. t1.setStudents(ss);  //1
  39. t2.setStudents(ss);  //1
  40. //
  41. //          s1.setTeachers(ts);  //2
  42. //          s2.setTeachers(ts);  //2
  43. s = HibernateUtil.getSession();
  44. tx = s.beginTransaction();
  45. s.save(t1);
  46. s.save(t2);
  47. s.save(s1);
  48. s.save(s2);
  49. tx.commit();
  50. } finally {
  51. if (s != null)
  52. s.close();
  53. }
  54. }
  55. }

运行此程序后:控制台打印的sql语句如下所示:

Hibernate: insert into Teacher (name) values (?)

Hibernate: insert into Teacher (name) values (?)

Hibernate: insert into Student (name) values (?)

Hibernate: insert into Student (name) values (?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

一共在中间表里面插入了4条记录。

中间表结构如下所示:

DROP TABLE IF EXISTS `test`.`teacher_student`;

CREATE TABLE  `test`.`teacher_student` (

  `teacher_id` int(11) NOT NULL,

  `student_id` int(11) NOT NULL,
  PRIMARY KEY (`student_id`,`teacher_id`),

  KEY `FK2E2EF2DE6C8A2663` (`teacher_id`),

  KEY `FK2E2EF2DE5BEEDBC3` (`student_id`),

  CONSTRAINT `FK2E2EF2DE5BEEDBC3` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`),

  CONSTRAINT `FK2E2EF2DE6C8A2663` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

表中插入的记录如下所示:

mysql> select * from teacher_student;

+------------+------------+

| teacher_id | student_id |

+------------+------------+

|          1 |          1 |

|          1 |          2 |

|          2 |          1 |

|          2 |          2 |

+------------+------------+

4 rows in set (0.00 sec)

程序中注释为1的语句非常重要,它是建立Teacher与Student关联的语句,如果没有这两条语句,虽然程序照样会执行,但是在中间表teacher_student没有任何记录,也就是Teacher与Student之间未关联。

当然你也可以通过程序中注释为2的语句来建立Teacher与Student之间的关联关系,同样会产生与注释为1的语句的效果。但是你不能在程序中同时出现以上四句程序,否则会抛出异常( PRIMARY KEY (`student_id`,`teacher_id`),所以会出现主键冲突的异常),:

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)

Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)

Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)

Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update

解决上面产生异常的办法是设置inverse属性。即在Tearcher一端或Student一端设置inverse="true",即让他们之中的某一方放弃维护关联关系。此时,虽然上面四句程序在测试程序中同时出现了(其实就是在对象模型上相互设置了他们的关联关系),但程序照样能运行正常,因为了在数据库模型上,只会有两句程序生效,也就是没有设置inverse="true"的那一端会去维护关联关系。有关inverse的说细信息,可以参看我的文章hibernate级联(cascade和inverse).

执行测试程序中的查询测试,控制台打印的信息如下所示:

Hibernate: select teacher0_.id as id5_0_, teacher0_.name as name5_0_ from Teacher teacher0_ where teacher0_.id=?

Hibernate: select students0_.teacher_id as teacher1_1_, students0_.student_id as student2_1_, student1_.id as id7_0_, student1_.name as name7_0_ from teacher_student students0_ left outer join Student student1_ on students0_.student_id=student1_.id where students0_.teacher_id=?

students:2

从打印出的sql语句可以看出,多对多关系进行查询时,效率是比较低的。

hibernate 多对多(many-to-many)的更多相关文章

  1. hibernate多对多关联映射

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  2. hibernate多对一双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  3. hibernate多对一单向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  4. Hibernate多对多关系映射(建表)

    下边讲述Hibernate多对多关系映射. 多对多关系的表的结构为: 两个实体表,还包含一个关系表,关系表为复合主键,如果要使用Hibernate多对多关系映射,则关系表必须只包含两个字段,如果生成了 ...

  5. atitit.atitit.hb many2one relate hibernate 多对一关联配置..

    atitit.atitit.hb many2one relate hibernate 多对一关联配置.. 1. 多对一单向 @ManyToOne 1 1. 其中@JoinColumn 注解 2 2.  ...

  6. hibernate 多对多

    HibernateHibernate多对多关联映射通常别拆分成两个多对一关联映射1. 下面的HostBean.UserBean.UserHostBean,UserHostBean是两个表之间的关联表, ...

  7. hibernate多对一单向外键

    hibernate多对一单向外键: 描述:

  8. Hibernate多对一ManytoOne

    ------------------------Hibernate多对一ManytoOne 要点: ManytoOne配置在多端 可以配置级联操作 @ManyToOne(cascade=Cascade ...

  9. hibernate 多对多一个对象出现多条记录问题

    hibernate 多对多时,当须要依据它关联的对象查找的时候,会出现一个对象有多条记录的问题 用 left join fetch 抓取查询的时候还是会出现这问题,是由于主表在关联表中有多条记录 用 ...

  10. Hibernate多对多操作

    ---------------------siwuxie095 Hibernate 多对多操作 以用户和角色为例 (一)多对多映射配置 第一步:创建两个实体类,用户和角色 第二步:让两个实体类之间互相 ...

随机推荐

  1. ros使用时的注意事项&技巧

    1.rosrun package-name executable-name 比如 rosrun turtlesim turtlesim_node 2.一旦启动roscore后,便可以运行ROS程序了. ...

  2. 构造函数与super

    1. 当不定义构造方法,系统会为类隐式的创建一个空的无参构造方法 2. 当类定义了有参的构造方法,系统就不会为类创建无参构造方法 3. 子类中,若不显式调用super(), super()会被隐式调用 ...

  3. 实验四 Android程序设计 实验报告 20162305李昱兴

    实验四 Android程序设计 实验报告 20162305李昱兴 一.Android Studio的安装测试 1.有关该软件 Android Studio,是基于Itellij IDEA的一款流行的I ...

  4. Block作为返回值时的使用

    昨天使用一个小例子简单说明了下Block作为参数时的使用. 今天再来复习一下Block作为返回值使用时的情况,先贴一小段热门第三方框架Masonry的官方sample code: [view1 mas ...

  5. javascript-电话薄小功能

    上代码: <style> *{ margin:0 ; padding: 0;} .phrase_wrap, .phrase_list{ width: 200px; height: 200p ...

  6. Linux内核中的信号机制--一个简单的例子【转】

    本文转载自:http://blog.csdn.net/ce123_zhouwei/article/details/8562958 Linux内核中的信号机制--一个简单的例子 Author:ce123 ...

  7. js实现继承的方式

    [原文] 前言 JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一.那么如何在JS中实现继承呢?让我们拭目以待. JS继承的实现方式 既然要实现继承,那么首先我们得有一个父类,代码如下: // ...

  8. U盘安装OS

    1. 老毛桃 2. 大白菜 3.

  9. scala学习手记27 - 下划线与参数

    在Scala里,下划线(_)可以表示函数值的参数.如果某个参数在函数里仅使用一次,就可以用下划线表示.每次在函数里用下划线,都表示随后的参数. val arr = Array(1, 2, 3, 4, ...

  10. PHP的可变变量名

    有时候可变的变量名会给编程带来很大的方便.也就是说变量名可以被动态的命名和使用.通常变量通过下面这样的语句来命名 : 1 2 3 <!--?php $a = 'hello'; ?--> 可 ...