九 多对多关系 学生<--->老师的关系
多对多关系 学生<--->老师的关系 一个学生可以有多个老师 一个老师可以有多个学生
在多对多的关系的表 在操作和性能方面都不太理想,所以多对多的映射使用较少,
实际使用中最好转换成一对多的对象模型;Hibernate会为我们创建中间关联表,转换成两个一对多。
具体操作是这样的 比如说我们建一个老师表 teacher 和一个学生表 student 怎么让这两个表连成有关系的表呢
可以这样去设计 设计一个中间表teacher_student 中间表里仅仅存的是teacher的id 和student的id 并且建一个主键,
主键是这两个键的组合键 我们只要把这两个id作为外键的形式存放在这个中间表中便可以了 然后由这个中间表去
跟teacher和student表建立关系便可以,这个关系就是一对多的关系 一个中间表跟两个表建立关系
那现在我们开始吧
先设计模型
Teacher类
package hibernate.many;
import java.util.Set;
public class Teacher {
private int id;
private String name;
private Set<Student> students;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
映射文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="hibernate.many">
<class name="Teacher">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" table="teacher_student">
<key column="teacher_id"/>
<many-to-many class="Student" column="student_id"/>
</set>
</class>
</hibernate-mapping>
分析:比如说我们要根据teacher去查找student的信息 我们该怎样去查呢
首先我们查找到teacher的信息 我们根据这个teacher的id 去查teacher_student中间表的数据
这样就可以查出student_id 这个student_id就是student的id 现在我们查学生就不难查出来了吧
看上面的配置 这是个实体类的映射文件 注意看set部分 set存的是什么 就是这个Teacher实体存的属性
属性名是什么 是students 看students其实就是这个实体的属性 上面class节点不是已经有默认的对应的表
吗?默认的对应的表也就是这个实体的表对应的表是小写的teacher名的表 那这里这个table是干嘛的呢
这个啊 就是指定的中间表啊 这个中间表teacher_student在这里是一个条件存在的 说的是如果通过查老师
就可以查中间表了,查询的条件是什么呢?看key节点了吧 这个节点就是查询的条件,指的是这个中间表引用
当前Teacher的id作为外键存放到中间表的 好,现在就查中间表了,现在进一步分析 那many-to-many节点是什么意思呢
这里指定的是,告诉hibernate现在要查这个中间表的下一个表的条件 class是告诉现在还可以查的是什么表
class="Student" 哈哈,原来啊,这个set存放的Student这个实体啊 那要查这个Student实体的数据,该怎么查呢
现在不是又告诉你条件了吗?看column告诉你条件是student_id就可以查Student的数据,指定这个column就是告诉
hibernate这个中间表的字段student_id是引用Student实体的id作为外键而存放的 到这步,数据不全都出来了吗
小结:发现这个set指定的都是teacher_student的字段 指定的两个列都是作为查询条件而存在的 现在理解了吧!并且在
注意哦!这里指定的一切列名都不在设计的实体哦
Student类
package hibernate.many;
import java.util.Set;
public class Student {
private int id;
private String name;
private Set<Teacher> teacher;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Teacher> getTeacher() {
return teacher;
}
public void setTeacher(Set<Teacher> teacher) {
this.teacher = teacher;
}
}
映射文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="hibernate.many">
<class name="Student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="teacher" table="teacher_student">
<key column="student_id"/>
<many-to-many class="Teacher" column="teacher_id"/>
</set>
</class>
</hibernate-mapping>
分析,这里可以按照上面的那种分析原理便可
测试
package hibernate.test;
import java.util.HashSet;
import java.util.Set;
import hibernate.many.Student;
import hibernate.many.Teacher;
import hibernate.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.jboss.util.HashCode;
public class ManyToMany {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
add();
query(1);
}
static void add(){
Session session=null;
Transaction tx=null;
try{
session=HibernateUtil.getSession();
tx=session.beginTransaction();
Set<Teacher> t=new HashSet<Teacher>();
Teacher t1=new Teacher();
t1.setName("曾笑先老师");
t.add(t1);
Teacher t2=new Teacher();
t2.setName("莫如非老师");
t.add(t2);
Set<Student> s=new HashSet<Student>();
Student s1=new Student();
s1.setName("潘长江学生");
s.add(s1);
Student s2=new Student();
s2.setName("如烟学生");
s.add(s2);
t1.setStudents(s);
t2.setStudents(s);
// s1.setTeacher(t);
// s2.setTeacher(t);
session.save(t1);
session.save(t2);
session.save(s1);
session.save(s2);
tx.commit();
}finally{
if(session!=null) session.close();
}
}
static void query(int id){
Session session=null;
try{
session=HibernateUtil.getSession();
Teacher t=(Teacher)session.get(Teacher.class, 1);
System.out.println(t.getName()+"-------->"+t.getStudents().size());
}finally{
if(session!=null) session.close();
}
}
}
分析:s1.setTeacher(t);s2.setTeacher(t); t1.setStudents(s); t2.setStudents(s);为什么不能都执行呢?
这跟中建表teacher_student的表结构有关 表结构的主键是以Teacher的id和Student的id组合键作为id的,
如果都执行就会出现主键重复异常的信息 现在来看ddl语句
CREATE TABLE `teacher_student` (
`student_id` int(11) NOT NULL,
`teacher_id` int(11) NOT NULL,
PRIMARY KEY (`teacher_id`,`student_id`),
KEY `FK2E2EF2DE28DF55E7` (`teacher_id`),
KEY `FK2E2EF2DE18440B47` (`student_id`),
CONSTRAINT `FK2E2EF2DE18440B47` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`),
CONSTRAINT `FK2E2EF2DE28DF55E7` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
现在就知道了吧 当你执行完s1.setTeacher(t);s2.setTeacher(t);后,如果再次t1.setStudents(s); t2.setStudents(s);
执行,hibernate接受的teacher的id和Student的id其实跟前两句是一样的,组合成的主键也是一样的 ,这不就违反主键约束了吗
总结:这种多对多或者一对多在程序的性能上是比较低的,在使用的时候,要特别注意,我们可以去使用单向的一对多或者多对多
多对多查询一条数据 往往查询的是两张表,这在应用中其实是比较少的,如果数据量特别庞大,会出现很严重的问题
尽量要用单向的多对多或者一对多,单向的一对一,只需在映射文件中配置一方即可,配置哪一方只需考虑其对应的一方的数据量
完毕 end!