您的位置:首页 > 其它

hibernate练习之双向关联一对多

2014-09-03 10:11 369 查看
实体类:

一个班级对应多个学生,一个学生对应一个班级。

一的一方为班级,多的一方为学生,在多的一方设置外键(也就是在多的一方的实体类中写一的一方的引用),在一的一方实体类中写集合,集合中的元素为多的一方。

在映射文件中多的一方配置<many-to-one>标签,在一的一方配置集合,

如 <set name="students" cascade="all" inverse="false">

<key>

<!--

通过classes建立与student之间的联系

-->

<column name="cid"></column>

</key>

<one-to-many class="com.oneToMany.Student"/>

</set>

班级的实体类:Classes

package com.oneToMany;

import java.io.Serializable;
import java.util.Set;

public class Classes implements Serializable{
private Long cid;
private String cname;
private String description;
private Set<Student> students;//一的一方写集合(因为含有多个相同的)

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public Long getCid() {
return cid;
}

public void setCid(Long cid) {
this.cid = cid;
}

public String getCname() {
return cname;
}

public void setCname(String cname) {
this.cname = cname;
}

public Set<Student> getStudents() {
return students;
}

public void setStudents(Set<Student> students) {
this.students = students;
}
}
学生的实体类:

package com.oneToMany;

import java.io.Serializable;

public class Student implements Serializable{
private Long sid;
private String sname;
private Classes classes;//多的一方写 一的一方的引用(也就是定义一个一的一方的类变量)

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

private String description;

public Long getSid() {
return sid;
}

public void setSid(Long sid) {
this.sid = sid;
}

public String getSname() {
return sname;
}

public void setSname(String sname) {
this.sname = sname;
}

public Classes getClasses() {
return classes;
}

public void setClasses(Classes classes) {
this.classes = classes;
}

}


映射文件:

班级的映射文件:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.oneToMany.Classes">
<id name="cid" type="java.lang.Long" length="5">
<column name="cid"></column>
<generator class="increment"></generator>
</id>

<property name="cname" type="java.lang.String" length="20"></property>
<property name="description" type="java.lang.String" length="100"></property>
<!--
cascade指的是对象对对象的操作
inverse指的是对象对关系的操作
inverse是用来维护关系的
*  要么是一对多的关系
*  要么多对多的关系
*  谁来维护关系
inverse所在的映射文件对应的持久化对象维护关系
默认值是false  表明维护关系
true  表明不维护关系
-->
<set name="students" cascade="all" inverse="false">
<key>
<!--
通过classes建立与student之间的联系
-->
<column name="cid"></column>
</key>
<one-to-many class="com.oneToMany.Student"/>
</set>
</class>
</hibernate-mapping>


学生的映射文件:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.oneToMany.Student">
<id name="sid" type="java.lang.Long" length="5">
<column name="sid"></column>
<generator class="increment"></generator>
</id>

<property name="sname" type="java.lang.String" length="20"></property>
<property name="description" type="java.lang.String" length="100"></property>
<!--
多对一
注意:在many-to-one中没有inverse属性
对student表的修改本身就是维护外键
-->
<many-to-one name="classes" class="com.oneToMany.Classes" cascade="all">
<!--
外键
描述了通过student建立与classes之间的联系
-->
<column name="cid"></column>
</many-to-one>
</class>
</hibernate-mapping>


配置文件:hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- 是用来描述数据库的连接 -->
<session-factory>
<!--
驱动
-->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!--
url
-->
<property name="connection.url">
jdbc:mysql://localhost:3306/hibernate
</property>
<!--
username
-->
<property name="connection.username">root</property>
<!--
password
-->
<property name="connection.password">root</property>
<!--
hibernate针对建表的操作
update  如果有表,检查表的结构,如果没有则创建
create-drop 启动hibernate创建表,结束hibernate删除表
create  每次启动都重新创建表
validate 每次启动都检查表的结构
-->
<property name="hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<mapping resource="com/oneToMany/Classes.hbm.xml" />
<mapping resource="com/oneToMany/Student.hbm.xml" />
</session-factory>
</hibernate-configuration>


利用Junit进行测试

package com.oneToMany;

import java.util.HashSet;

import java.util.List;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
/**
* 1、保存班级
* 2、保存学生
* 3、保存班级的时候同时保存学生
* 4、保存班级的时候同时保存学生,并且建立班级和学生之间的关系
* 5、已经存在一个班级,新建一个学生,并且建立该学生和该班级之间的关系
* 6、已经存在一个学生,新建一个班级,并且建立该学生和该班级之间的关系
* 7、已经存在一个学生,已经存在一个班级,解除该学生和原来班级之间的关系,建立该学生和新班级之间的关系
* 8、已经存在一个学生,解除该学生和该学生所在班级之间的关系
* 9、解除该班级和所有的学生之间的关系,再重新建立该班级和一些新的学员之间的关系
* 10、解除该班级和所有的学生之间的关系
* 11、删除班级
*      *
*      	*  解除该班级和所有的学生之间的关系
*      	*  删除该班级
*      *
*          删除班级的同时删除学生
* 12、删除学生
*      同删除班级
* @author Administrator
*
*/
public class OneToManyDoubleTest {
private static SessionFactory sf= null;
static{
Configuration  configuration = new Configuration();//获取Configuration实例
configuration.configure("com/oneToMany/hibernate.cfg.xml");//加载指定路径下的配置文件
sf = configuration.buildSessionFactory();//创建一个session工厂,利用工厂产生session
}

/*
加载方式2
SessionFactory sf = new Configuration().configure()
.addClass(Classes.class)
.addClass(Student.class)
.buildSessionFactory();*/

/**
* 3、保存班级的时候同时保存学生
* 		Hibernate: select max(cid) from Classes
Hibernate: select max(sid) from Student
Hibernate: insert into Classes (cname, description, cid) values (?, ?, ?)
Hibernate: insert into Student (sname, description, cid, sid) values (?, ?, ?, ?)
Hibernate: update Student set cid=? where sid=?
更新外键
说明:
classes.setStudents(students);  通过classes来维护关系 ,要发出update语句,更新外键
*/
@Test
public void testSaveClasses_Cascade_S(){
Session session = sf.openSession();
session.beginTransaction();

Classes classes = new Classes();
classes.setCname("计科1");

Student stu = new Student();
stu.setSname("张2");

Set<Student> students = new HashSet<Student>();
students.add(stu);

//	classes.setStudents(students);
stu.setClasses(classes);
session.save(classes);

session.getTransaction().commit();
session.close();
}

/**
* 已经存在一个班级,新建一个学生,并且建立该学生和该班级之间的关系
*/
@Test
public void testSaveStudent_R(){
Session session = sf.openSession();
session.beginTransaction();

//通过班级关联学生
//取出班级
/*Classes classes = (Classes) session.get(Classes.class, 1L);

//新建一个学生
Student stu = new Student();
stu.setSname("张三");

//建立学生和班级之间的关系
classes.getStudents().add(stu);*/

//通过学生关联班级
Classes classes = (Classes) session.get(Classes.class, 2L);
Student stu = new Student();
stu.setSname("张四");
stu.setDescription("好哥们");

stu.setClasses(classes);
session.save(stu);

session.getTransaction().commit();
session.close();
}

/**
* 已经存在一个学生,新建一个班级,并且建立该学生和该班级之间的关系
*   通过分析:
*      *  因为存在建立关系的操作,所以操作学生端效率比较高
*      *  在这里存在保存班级的操作,所以应该是通过更新学生级联保存班级
*      可以通过学生和班级建立关联关系,也可以通过班级和学生建立关联关系
*/
@Test
public void testUpdateStudent_CasCade(){
Session session = sf.openSession();
session.beginTransaction();

//获取一个学生
Student stu = (Student) session.get(Student.class, 2L);

Classes classes = new Classes();
classes.setCname("一般");
classes.setDescription("不错");

/*	Set<Student> students = new HashSet<Student>();
students.add(stu);
classes.setStudents(students);*/

//stu.setClasses(classes);
session.save(classes);

session.getTransaction().commit();
session.close();
}
/**
* 已经存在一个学生,已经存在一个班级,解除该学生和原来班级之间的关系,建立该学生和新班级之间的关系
*/
@Test
public void testRebuild_R(){
Session session = sf.openSession();
session.beginTransaction();

Student stu = (Student) session.get(Student.class, 1L);
Classes classes = (Classes) session.get(Classes.class, 2L);
//因为sutdent已经是持久化类,所以可以不调用save方法
stu.setClasses(classes);

session.getTransaction().commit();
session.close();
}

/**
* 已经存在一个学生,解除该学生和该学生所在班级之间的关系
*/
@Test
public void testRealse_R(){
Session session = sf.openSession();
Transaction transaction = session.beginTransaction();
Student student = (Student)session.get(Student.class, 2L);
student.setClasses(null);
transaction.commit();
session.close();
}

/**
* 解除该班级和所有的学生之间的关系,再重新建立该班级和一些新的学员之间的关系
*/
@Test
public void testRealse_Rebuild_R(){
Session session = sf.openSession();
session.beginTransaction();

Student stu1 = new Student();
stu1.setSname("新学员1");
Student stu2 = new Student();
stu2.setSname("新学员2");

/*
查询出在班级中的学生,并移出集合的做法
Classes classes = (Classes) session.get(Classes.class, 2L);
List list = session.createQuery("from Student where sid in(1,3)").list();
classes.getStudents().removeAll(list);

classes.getStudents().add(stu1);
classes.getStudents().add(stu2);*/
/**
* 1、获取班级
* 2、获取该班级的所有的学生
* 3、遍历学生,把学生的班级设置为null
* 4、新建两个学员
* 5、建立两个学员与班级之间的关系
*/
Classes classes = (Classes) session.get(Classes.class, 2L);
Set<Student> students = classes.getStudents();
for(Student stu:students){//迭代并将所有的学生与该班级解除关系
stu.setClasses(null);
}
students.add(stu1);
students.add(stu2);
/**
*	   当发生transaction.commit的时候,hibernate内部会检查所有的持久化对象
*   会对持久化对象做一个更新,因为classes是一个持久化状态的对象,所以hibernate
*   内部要对classes进行更新,因为在classes.hbm.xml文件中<set name="students" cascade="all" inverse="true">
*   意味着在更新classes的时候,要级联操作student,而student是一个临时状态的对象
*   所以要对student进行保存,在保存student的时候,就把外键更新了
*/
session.getTransaction().commit();
session.close();
}
/**
* 解除该班级和所有的学生之间的关系
*/
@Test
public void testRealseAll_R(){
Session session = sf.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes)session.get(Classes.class, 1L);
Set<Student> students = classes.getStudents();
for(Student student:students){
student.setClasses(null);
}
transaction.commit();
session.close();
}

/**
* 先解除关系,再删除班级
*/
@Test
public void testDeleteClass_1(){
Session session = sf.openSession();
session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 2L);
Set<Student> students = classes.getStudents();
for(Student stu:students){//迭代将和与该班级所有的学生设置为null,即解除关系
stu.setClasses(null);
}

session.delete(classes);//删除班级

session.getTransaction().commit();
session.close();
}

/**
* 在删除班级的时候,同时删除整个班级的学生
*/

@Test
public void teetDeleteClass(){
Session session = sf.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes)session.get(Classes.class, 7L);

session.delete(classes);

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

//12、删除学生 同删除该学生所在班级
@Test
public void teetDeleteStudent(){
Session session = sf.openSession();
session.beginTransaction();

Student stu = (Student) session.get(Student.class, 1L);
session.delete(stu);

session.getTransaction().commit();
session.close();
}

}
总结:

一对多的操作:

* hibernate是通过客户端的代码参照映射文件来决定怎么样去数据库进行操作

* 一对多的双向:既能通过Classes.hbm.xml文件联系到student,也能通过student.hbm.xml文件联系到

classes,在类中也要体现双向的关系

* session.save/update(classes),hibernate内部会参照classes.hbm.xml文件,这个时候student.hbm.xml

映射文件不起作用,反之也成立

* 在hibernate一对多的关系中,只有一的一端的set集合中有inverse属性,多的一端没有inverse属性

* 在hibernate中,inverse属性和数据库的外键对应

* 在Classes.hbm.xml文件中,set集合,inverse的值如果为true,说明classes放弃关系的维护,如果为false,

则负责维护关系,但是多的一方维护关系效率比较高

* cascade说明指的是对象与对象之间的操作,和外键没有关系

* 处于持久化状态的对象在session中,在客户端不需要做session.save/update操作,hiernate内部会自动去检查

持久化状态的对象的属性是否发生改变,如果改变则发出update语句,如果没有改变则不会发出update语句。如果

该对象是一的一方,在一的一方的映射文件中有cascade="all"时,hibernate内部还会检查该持久化对象关联的

集合,对此集合做update/save操作。但是整个操作和外键没有关系。只有当通过多的一方建立关系以后,才能使

外键有值

* 一般一对多设计到关系的维护,都是通过多的一方来操作的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: