Hibernate框架详解(三)

表与表之间的关系

(1)一对一

例子:在中国,一个男人只能有一个妻子,一个女人只能有一个丈夫。

 

(2)一对多

例子:学生和班级的关系,一个班级可以有多个学生,而一个学生只能属于一个班级。

一对多建表:通过外键建立关系,在多的那一方创建字段作为外键,指向一的那一方的主键。

 

(3)多对多

例子:学生和课程的关系,一个学生可以选择多个课程,一个课程可以有多个学生。

多对多建表:创建第三章表维护关系,至少有两个字段作为外键,指向两个表的主键。

 

Hibernate的一对多操作(重点)

(1)一对多的映射配置

以上述班级和学生的关系为例:班级是一,学生是多。

第一步:创建两个实体类,学生类和班级类

public class Student {
	private Integer sid;
	private String sname;
	private String address;
	private String phone;
	private String sex;
	private Classes classes;
	
	
	public Integer getSid() {
		return sid;
	}
	public void setSid(Integer sid) {
		this.sid = sid;
	}
	public String getSname() {
		return sname;
	}
	public void setSname(String sname) {
		this.sname = sname;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public Classes getClasses() {
		return classes;
	}
	public void setClasses(Classes classes) {
		this.classes = classes;
	}
}
public class Classes {
	private Integer cid;
	private String cname;
	private String location;
	private Set<Student> cStudents=new HashSet<Student>();
	
	
	
	public Integer getCid() {
		return cid;
	}
	public void setCid(Integer cid) {
		this.cid = cid;
	}
	public String getCname() {
		return cname;
	}
	public void setCname(String cname) {
		this.cname = cname;
	}
	public String getLocation() {
		return location;
	}
	public void setLocation(String location) {
		this.location = location;
	}
	public Set<Student> getcStudents() {
		return cStudents;
	}
	public void setcStudents(Set<Student> cStudents) {
		this.cStudents = cStudents;
	}
}

第二步:让两个实体类之间互相表示

在班级类里面表示多个学生,即一个班级里有多个学生:

注意:在hibernate中要求使用集合表示多的数据,使用set集合

在学生类里面表示所属班级,即一个学生只能属于一个班级:

第三步:配置映射关系

  • 一般一个实体类对应一个映射文件
  • 首先完成最基本的配置,然后配置一对多的关系

在班级映射文件中,配置所有学生:(hibernate机制:双向维护外键,即一对多双方都需要配置外键)

使用<set>标签标示所有学生,其中<set>标签里面的name属性标示班级类中学生的set集合名称。

使用<key>标签表示外键(一对多建表,有外键),其中column属性标示外键名称

<one-to-many>标签表示一对多的关系,其中class属性标示学生类全路径

<hibernate-mapping>
	<class name="cn.itcast.entity.Classes" table="classes">
		<id name="cid" column="cid">
			<generator class="native"></generator>
		</id>
		<property name="cname" column="cname"></property>
		<property name="location" column="location"></property>
		
		<set name="cStudents">
			<key column="csid"></key>
			<one-to-many class="cn.itcast.entity.Student"/>
		</set>
	</class>
</hibernate-mapping>

在学生映射文件中,配置所属班级:(hibernate机制:双向维护外键,即一对多双方都需要配置外键)

使用<many-to-one>标签表示多对一的关系,其中name属性表示班级类对象名称,class属性表示班级类全路径,column属性表示外键名称,与上述<key>标签中column属性一致。

<hibernate-mapping>
	<class name="cn.itcast.entity.Student" table="student">
		<id name="sid" column="sid">
			<generator class="native"></generator>
		</id>
		<property name="sname" column="sname"></property>
		<property name="address" column="address"></property>
		<property name="phone" column="phone"></property>
		<property name="sex" column="sex"></property>
		<many-to-one name="classes" class="cn.itcast.entity.Classes" column="csid"></many-to-one>
	</class>
</hibernate-mapping>

第四步: 创建核心配置文件,把映射文件引入到核心配置文件中

<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///test1016</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">root</property>
	
	
		<property name="hibernate.show_sql">true</property>
	    <property name="hibernate.format_sql">true</property>
		<property name="hibernate.hbm2ddl.auto">update</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		
	
		<mapping resource="cn/itcast/entity/classes.hbm.xml"/>
		<mapping resource="cn/itcast/entity/student.hbm.xml"/>
	</session-factory>
</hibernate-configuration>

第五步:编写测试代码

public class Operation {
	public void addStudent() {
		Configuration cfg=new Configuration();
		cfg.configure();
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		Transaction tx=session.beginTransaction();
		Student student=new Student();
		student.setSname("张三");
		student.setSex("男");
		student.setPhone("56154");
		student.setAddress("北京");
		student.setClasses(null);
		session.save(student);
		tx.commit();
		session.close();
		sessionFactory.close();
	}
	
	
	
	public void addClasses(){
		Configuration cfg=new Configuration();
		cfg.configure();
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		Transaction tx=session.beginTransaction();
		Classes classes=new Classes();
		classes.setCname("一班");
		classes.setLocation("左家垅");
		classes.setcStudents(null);
		session.save(classes);
		tx.commit();
		session.close();
		sessionFactory.close();
	}
}

结果:

 

(2)一对多的级联操作

级联操作:

级联操作是指当主控方执行保存,更新或者删除操作时,其关联对象(被控方)也执行相同的操作。

  • 级联保存:添加一个班级,为这个班级添加多个学生
  • 级联删除:删除一个班级,这个班级里面的所有学生也删除

一对多级联保存

需求:添加班级,为这个班级添加一个学生

复杂写法:(底层写法)

public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction transaction=session.beginTransaction();
		
		
		//创建班级对象
		Classes classes=new Classes();
		classes.setCname("火箭班");
		classes.setLocation("china");
		
		
		//创建学生对象
		Student student=new Student();
		student.setSname("张三");
		student.setSex("男");
		student.setPhone("1123");
		student.setAddress("左家垅");
		
		
		//建立班级和学生对象的关系,即把学生对象放到班级对象中,把班级对象放到学生对象汇总
		classes.getcStudents().add(student);
		student.setClasses(classes);
		
		
		//保存到数据库
		session.save(classes);
		session.save(student);
		

		transaction.commit();
		session.close();
		sessionFactory.close();	
	}

简化写法:

需求:上述复杂写法,需要维护双向关系,即班级对象维护学生对象,学生对象维护班级对象。当一个班级中有很多学生对象时,需要挨个进行维护。我们在班级映射文件中,使用cascade属性设置控制方对关联对象采用级联操作,只需关注主控方即可。

第一步:在班级映射文件中的<set>标签进行配置

第二步:创建班级和学生对象,只需要把学生对象放入到班级对象中,最后保存班级对象即可。

public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction transaction=session.beginTransaction();
		
		
		//创建班级对象
		Classes classes=new Classes();
		classes.setCname("就业班");
		classes.setLocation("香港");
		
		
		//创建学生对象
		Student student=new Student();
		student.setSname("王富贵");
		student.setSex("男");
		student.setPhone("99999");
		student.setAddress("瓜瓢山");
		
		
		//将学生对象添加到班级对象中
		classes.getcStudents().add(student);
		
		
		//只需保存班级对象
		session.save(classes);
		

		transaction.commit();
		session.close();
		sessionFactory.close();		
	}

总结:一对多的关系中,一是主控方,所以在主控方的映射文件中配置被控方的级联操作。其次将被控方对象加入到主控方对象中,最后也不需提交主控方对象即可。(只需关注主控方即可,主控方配置,主控方维护,主控方保存)

 

一对多级联删除

需求:删除某个班级,把班级中的所有学生删除

具体实现:

  • 第一步:在班级映射文件<set>标签,使用cascade属性值delete进行配置
  • 第二步:根据id查询对象那个,调用session中的delete方法删除班级对象

代码:

<hibernate-mapping>
	<class name="cn.itcast.entity.Classes" table="classes">
		<id name="cid" column="cid">
			<generator class="native"></generator>
		</id>
		<property name="cname" column="cname"></property>
		<property name="location" column="location"></property>
		
		<set name="cStudents" cascade="save-update,delete">
			<key column="csid"></key>
			<one-to-many class="cn.itcast.entity.Student"/>
		</set>
	</class>
</hibernate-mapping>
public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		Transaction transaction=session.beginTransaction();
		
		
		Classes classes=session.get(Classes.class,6);
		session.delete(classes);
		

		transaction.commit();
		session.close();
		sessionFactory.close();
	}

结果:

执行过程:

(1)根据id查询班级

(2)根据外键id值查询学生

(3)把学生外键设置为null

(4)删除班级和学生

总结:一对多的关系中,只需关注主控方即可。级联保存的时候,在主控方进行配置级联操作save-update,同时在主控方添加被控方对象,最后提交主控方对象即可。级联删除的时候,在主控方进行配置级联操作delete,同时查询主控方对象,并删除即可实现级联删除。

 

双向关联产生多余的SQL语句(使用inverse属性,让“一”方放弃外键维护权)

需求:让李翠花所属班级调整为火箭班

代码:

public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction transaction=session.beginTransaction();
		
		Classes classes=session.get(Classes.class, 3);
		Student student=session.get(Student.class, 4);
		
		student.setClasses(classes);
		classes.getcStudents().add(student);
		

		transaction.commit();
		session.close();
		sessionFactory.close();
	}

结果:

问题:Hibernate双向维护外键,在班级和学生里面都需要维护外键,修改班级的时候,修改一次外键;修改学生的时候,修改一次外键,造成效率问题。

上述修改操作,执行了两次update语句------------产生原因是因为双向维护了关系,更新班级的时候会修改一次外键,更新学生的时候也会修改一次外键,这样就产生了多余的SQL。

解决:其中一方放弃外键维护权即可。通常在一对多的关系中,让“一”的一方放弃外键维护权。举个例子:一个班级对应多个学生,一个学生只属于一个班级,这是典型的一对多的关系。要让一个班级记住所有学生的名字是很难的,但是如果让每个学生记住班级的名字就很简单。所以在一对多的关系中,“一”的一方放弃外键的维护权。

实现:使用inverse属性进行配置,inverse属性默认值是false,即不放弃外键维护权;配置为true,则代表放弃外键维护权。

代码:

<hibernate-mapping>
	<class name="cn.itcast.entity.Classes" table="classes">
		<id name="cid" column="cid">
			<generator class="native"></generator>
		</id>
		<property name="cname" column="cname"></property>
		<property name="location" column="location"></property>
		
		<set name="cStudents" cascade="save-update,delete" inverse="true">
			<key column="csid"></key>
			<one-to-many class="cn.itcast.entity.Student"/>
		</set>
	</class>
</hibernate-mapping>
public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction transaction=session.beginTransaction();
		
		//根据id查询调整后的班级对象
		Classes classes=session.get(Classes.class, 4);
		//根据id查询要调整的学生对象
		Student student=session.get(Student.class, 3);
		
		//维护双向关系---将学生添加到班级中,将班级添加到学生中
		student.setClasses(classes);
		classes.getcStudents().add(student);
		

		transaction.commit();
		session.close();
		sessionFactory.close();
	
	}

结果:(只进行了一次update)

 

Hibernate的多对多操作(重点)

(1)多对多的映射配置

以上述学生和课程的关系为例:学生是多,课程也是多。

第一步:创建两个实体类,学生类和课程类

public class Course {
	private Integer cid;
	private String cname;
	private String description;
	private Set<Student> cStudents=new HashSet<Student>();
	
	
	public Set<Student> getcStudents() {
		return cStudents;
	}
	public void setcStudents(Set<Student> cStudents) {
		this.cStudents = cStudents;
	}
	
	public Integer getCid() {
		return cid;
	}
	public void setCid(Integer cid) {
		this.cid = cid;
	}
	public String getCname() {
		return cname;
	}
	public void setCname(String cname) {
		this.cname = cname;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
}
public class Student {
	private Integer sid;
	private String sname;
	private String address;
	private String phone;
	private String sex;
	private Set<Course> sCourses=new HashSet<Course>();

	
	
	public Set<Course> getsCourses() {
		return sCourses;
	}
	public void setsCourses(Set<Course> sCourses) {
		this.sCourses = sCourses;
	}
	
	public Integer getSid() {
		return sid;
	}
	public void setSid(Integer sid) {
		this.sid = sid;
	}
	public String getSname() {
		return sname;
	}
	public void setSname(String sname) {
		this.sname = sname;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
}

第二步:让两个实体类互相表示

一个学生里面表示所有课程,使用set集合

一个课程里面表示所有学生,使用set集合

第三步:配置映射关系

  • 一般一个实体类对应一个映射文件
  • 首先完成最基本的配置,然后配置多对多的关系

在学生映射文件中,配置所有课程:

<hibernate-mapping>
	<class name="cn.itcast.entity.Student" table="student">
		<id name="sid" column="sid">
			<generator class="native"></generator>
		</id>	
		<property name="sname" column="sname"></property>
		<property name="address" column="address"></property>
		<property name="phone" column="phone"></property>
		<property name="sex" column="sex"></property>
		
		<!-- 在学生里面表示所有课程,使用set标签 -->
		<!-- name属性:课程set集合名称 -->
		<!-- table属性:第三张表名称(维护两张表的关系) -->		
		<set name="sCourses" table="student_course">
		
			<!-- key标签配置当前映射文件在第三张表中外键的名称 -->
			<key column="studentid"></key>
			
			<!-- many-to-many标签配置多对多的关系 -->
			<!-- class属性:课程类全路径 -->
			<!-- column属性:课程在第三张表外键名称,与课程类配置文件中的key标签属性一致 -->
			<many-to-many class="cn.itcast.entity.Course" column="courseid"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

在课程映射文件中,配置所有学生:

<hibernate-mapping>
	<class name="cn.itcast.entity.Course" table="course">
		<id name="cid" column="cid">
			<generator class="native"></generator>
		</id>
		<property name="cname" column="cname"></property>
		<property name="description" column="description"></property>

		<!-- 在课程里面表示所有学生,使用set标签 -->
		<!-- name属性:学生set集合名称 -->
		<!-- table属性:第三张表名称(维护两张表的关系) -->		
		<set name="cStudents" table="student_course">
		
			<!-- key标签配置当前映射文件在第三张表中外键的名称 -->
			<key column="courseid"></key>
			
			<!-- many-to-many标签配置多对多的关系 -->
			<!-- class属性:学生类全路径 -->
			<!-- column属性:学生在第三张表外键名称,与学生类配置文件中的key标签属性一致 -->
			<many-to-many class="cn.itcast.entity.Student" column="studentid"></many-to-many>
		</set>	
	</class>
</hibernate-mapping>

第四步: 创建核心配置文件,把映射文件引入到核心配置文件中

<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///test1017</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">root</property>
	
	
		<property name="hibernate.show_sql">true</property>
	    <property name="hibernate.format_sql">true</property>
		<property name="hibernate.hbm2ddl.auto">update</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		
		
		
		<mapping resource="cn/itcast/entity/course.hbm.xml"/>
		<mapping resource="cn/itcast/entity/student.hbm.xml"/>

	</session-factory>
</hibernate-configuration>

第五步:编写测试代码

public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction tx=session.beginTransaction();
		
		Student student1=new Student();
		student1.setSname("王富贵");
		student1.setSex("男");
		student1.setPhone("185648411");
		student1.setAddress("左家垅");
		
		Student student2=new Student();
		student2.setSname("王翠花");
		student2.setSex("女");
		student2.setPhone("8435184");
		student2.setAddress("瓜瓢山");
		
		
		
		Course course1=new Course();
		course1.setCname("高数");
		course1.setDescription("这门课非常难");
		
		Course course2=new Course();
		course2.setCname("物联网");
		course2.setDescription("考试非常简单");
		
		Course course3=new Course();
		course3.setCname("计算机网络");
		course3.setDescription("非常有意思");
		
		student1.getsCourses().add(course1);
		student1.getsCourses().add(course3);
		student2.getsCourses().add(course3);
		student1.getsCourses().add(course2);
		
		session.save(student1);
		session.save(student2);
		
		
		tx.commit();
		session.close();
		sessionFactory.close();
		
	}

结果:

 

(2)多对多的级联操作

级联操作:

级联操作是指当主控方执行保存,更新或者删除操作时,其关联对象(被控方)也执行相同的操作。

  • 级联保存:根据学生保存课程,或者根据课程保存学生
  • 级联删除:

多对多级联保存

需求:根据学生保存课程

简化写法:

第一步:在学生映射文件中的<set>标签进行配置,cascade属性值设置为save-update

第二步:创建学生和课程对象,把课程放到学生里面,最终保存学生就可以了

public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction tx=session.beginTransaction();
		
		Student student1=new Student();
		student1.setSname("王富贵");
		student1.setSex("男");
		student1.setPhone("185648411");
		student1.setAddress("左家垅");
		
		Student student2=new Student();
		student2.setSname("王翠花");
		student2.setSex("女");
		student2.setPhone("8435184");
		student2.setAddress("瓜瓢山");
		
		
		
		Course course1=new Course();
		course1.setCname("高数");
		course1.setDescription("这门课非常难");
		
		Course course2=new Course();
		course2.setCname("物联网");
		course2.setDescription("考试非常简单");
		
		Course course3=new Course();
		course3.setCname("计算机网络");
		course3.setDescription("非常有意思");
		
		student1.getsCourses().add(course1);
		student1.getsCourses().add(course3);
		student2.getsCourses().add(course3);
		student1.getsCourses().add(course2);
		
		session.save(student1);
		session.save(student2);
		
		
		tx.commit();
		session.close();
		sessionFactory.close();
		
	}

 

 

需求:根据课程保存学生

简化写法:

第一步:在课程映射文件中的<set>标签进行配置,cascade属性值设置为save-update

第二步:创建学生和课程对象,把学生放到课程里面,最终保存课程就可以了

	public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction tx=session.beginTransaction();
		
		Student student1=new Student();
		student1.setSname("王富贵");
		student1.setSex("男");
		student1.setPhone("185648411");
		student1.setAddress("左家垅");
		
		Student student2=new Student();
		student2.setSname("王翠花");
		student2.setSex("女");
		student2.setPhone("8435184");
		student2.setAddress("瓜瓢山");
		
		
		
		Course course1=new Course();
		course1.setCname("高数");
		course1.setDescription("这门课非常难");
		
		Course course2=new Course();
		course2.setCname("物联网");
		course2.setDescription("考试非常简单");
		
		Course course3=new Course();
		course3.setCname("计算机网络");
		course3.setDescription("非常有意思");
		
		course1.getcStudents().add(student1);
		course1.getcStudents().add(student2);
		course2.getcStudents().add(student1);
		course3.getcStudents().add(student2);
		
		session.save(course1);
		session.save(course2);
		session.save(course3);
		
		
		tx.commit();
		session.close();
		sessionFactory.close();
		
	}

 

多对多级联删除(一般不用,了解即可)

需求:删除某个课程,把课程中的所有学生也删除

第一步:在<set>标签中进行配置,其中cascade属性值设为delete

第二步:删除课程

 

(3)维护第三张表关系

多对多的关系主要是靠第三张表来维护的

需求:让某个学生选择某个课程

第一步:根据id查询学生和课程

第二步:把课程放到学生里面

	public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction tx=session.beginTransaction();
		
		Student student=session.get(Student.class, 1);
		Course course=session.get(Course.class, 6);
		student.getsCourses().add(course);
		
		tx.commit();
		session.close();
		sessionFactory.close();	
	}

需求:让某个学生放弃某个课程

第一步:根据id查询学生和课程

第二步:在学生中将课程去掉

	public void testDemo(){
		Configuration cfg=new Configuration();
		cfg.configure();
		
		SessionFactory sessionFactory=cfg.buildSessionFactory();
		Session session=sessionFactory.openSession();
		
		Transaction tx=session.beginTransaction();
		
//		Student student=session.get(Student.class, 1);
//		Course course=session.get(Course.class, 6);
//		student.getsCourses().add(course);
		
		Student student2=session.get(Student.class, 1);
		Course course2=session.get(Course.class, 2);
		student2.getsCourses().remove(course2);
		
		tx.commit();
		session.close();
		sessionFactory.close();	
	}

 

 

 

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值