九 多对多关系 学生<--->老师的关系

九 多对多关系 学生<--->老师的关系
多对多关系 学生<--->老师的关系 一个学生可以有多个老师 一个老师可以有多个学生
在多对多的关系的表 在操作和性能方面都不太理想,所以多对多的映射使用较少,
实际使用中最好转换成一对多的对象模型;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!

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值