多对多(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. ORM实例介绍

    http://blog.csdn.net/RonoTian/article/details/2900714

  2. Samba 3.6.9 安装、管理

    Samba简介 Samba服务类似于windows上的共享功能,可以实现linux上共享文件,windows上访问,当然在linux上可以访问到.是一种在局域网上共享文件和打印机的一种通信协议,它为局 ...

  3. Java实验五网络编程与安全

    实验五 网络编程与安全 实验准备 博客 活动一 两人一组结对编程: 0. 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA 1. 结对实现中缀 ...

  4. [BZOJ1018]堵塞的交通traffic

    Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一 ...

  5. Effective C++ 条款12:复制对象时勿忘其每一个成分

    void logCall(const std::string& funcName); class Customer { public: ... Customer (const Customer ...

  6. LeetCode——Longest Consecutive Sequence

    LeetCode--Longest Consecutive Sequence Question Given an unsorted array of integers, find the length ...

  7. DRBD分布式块设备复制

    一. DRBD介绍 1.1.数据镜像软件DRBD介绍分布式块设备复制(Distributed Relicated Block Deivce,DRBD),是一种基于软件.基于网络的块复制存储解决方案,主 ...

  8. 怎么用Python提取域名中的主域名

    从一个域名里面提取主域名,初想起来,貌似很简单,不就是数点[.]的个数吗?取最后一个点前后的字符串,那 abc.txt 是域名吗?那再加个验证,加上国家码,.com,.cn,.org结尾的才算,那这个 ...

  9. mysql实际使用思路

    在熟悉mysql语法的基础上,想在自己的应用程序中使用它,应该怎么操作呢? 自然的想法就是找到相应语言的mysql接口,然后熟悉接口,对其进行调用. 具体的做法与思路如下: 找到C的mysql接口 新 ...

  10. 2017版:KVM 性能优化之内存优化

    我们说完CPU方面的优化,接着我们继续第二块内容,也就是内存方面的优化.内存方面有以下四个方向去着手: EPT 技术 大页和透明大页 KSM 技术 内存限制 1. EPT技术 EPT也就是扩展页表,这 ...