`
woshixushigang
  • 浏览: 562232 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

hibernate 一对多one-to-many 单向 and 双向(many-to-one),inverse(反转)

阅读更多

hibernate 一对多 单向

classes---->student (one--->many)

类:

one

public class Classes {

private Integer id;
private String name;
private Set students;

}

many

public class Student {//不用动

private Integer id;
private String name;

}

 

hbm.xml

Classes.hbm.xml

<?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">
< hibernate-mapping package="com.model">
<class name="Classes" table="classes" >
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" type="java.lang.String" />
<set name="students">
<key column="class_id" />
<one-to-many class="com.model.Student" />
</set>

</class>

< /hibernate-mapping>

Student.hbm.xml(不用动)

<?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">
< hibernate-mapping package="com.zd.model">
<class name="Student" table="student" >
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" length="50" type="java.lang.String" />
</class>

< /hibernate-mapping>

 

测试用例:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.model.Classes;
import com.model.Student;


public class OneToManyTest extends TestCase {

public void testSave1(){
Session session = null;
Transaction ta = null;
try{
session = HibernateUtil.getSession();
ta = session.beginTransaction();
Student stu1 = new Student();
stu1.setName("z3");
Student stu2 = new Student();
stu2.setName("l4");
session.save(stu1); //一定要先保存,要不是瞬时对象
session.save(stu2);
Set stuSet = new HashSet();
stuSet.add(stu1);
stuSet.add(stu2);
Classes c = new Classes();
c.setName("Java Class");
c.setStudents(stuSet);
session.save(c);
ta.commit();
}catch(Exception e){
e.printStackTrace();
if(ta != null){
ta.rollback();
}
}finally{
//关闭session, user变为detached离线对象
HibernateUtil.closeSession(session);
}

}

Hibernate: insert into student (name) values (?)
Hibernate: insert into student (name) values (?)
Hibernate: insert into classes (name) values (?)
Hibernate: update student set class_id=? where id=?
Hibernate: update student set class_id=? where id=?

注:表student 的字段class_id必须null(若为not null,就会报错)。多了2条update, 所以one-to-many保存时,不太好,不推荐。



public void testGet1(){
Session session = null;
Transaction ta = null;
try{
session = HibernateUtil.getSession();
ta = session.beginTransaction();
Classes c = (Classes) session.get(Classes.class, new Integer(2));
System.out.println("Class.name=" + c.getName());
Set stuSet = c.getStudents();
if(stuSet != null && !stuSet.isEmpty()){
for(Iterator it = stuSet.iterator(); it.hasNext();){
Student s = (Student) it.next();
System.out.println("student.name=" + s.getName());
}
}
ta.commit();
}catch(Exception e){
e.printStackTrace();
if(ta != null){
ta.rollback();
}
}finally{
//关闭session, user变为detached离线对象
HibernateUtil.closeSession(session);
}

Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from classes classes0_ where classes0_.id=?
Class.name=Java Class
Hibernate: select students0_.class_id as class3_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_ from student students0_ where students0_.class_id=?
student.name=l4
student.name=z3

}

小结:one-to-may 是只配在one这端,但many另一端的表中要用外键与这相对应

<set name="students"> //name : one 中的set变量
<key column="class_id" /> //column 表student的外键字段class_id
<one-to-many class="com.model.Student" /> //class指many的类
</set>

 

===============================================================

双向(many-to-one) ,在多的一端配个多对一:

修改如下:
类:

public class Student {

private Integer id;
private String name;
private Classes classes;

 

hbm.xml

Student.hbm.xml

 

<?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">
< hibernate-mapping package="com.zd.model">
<class name="Student" table="student" >
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<property name="name" column="name" length="50" type="java.lang.String" />
<many-to-one name="classes" column="class_id"></many-to-one>
</class>

< /hibernate-mapping>

 

测试用例:

public void testSave2(){
Session session = null;
Transaction ta = null;
try{
session = HibernateUtil.getSession();
ta = session.beginTransaction();
Classes c = new Classes();
c.setName("php");
Student stu1 = new Student();
stu1.setName("a1");
stu1.setClasses(c);
Student stu2 = new Student();
stu2.setName("a2");
stu2.setClasses(c);
session.save(c); //先保存班级
session.save(stu1);
session.save(stu2);

ta.commit();
}catch(Exception e){
e.printStackTrace();
if(ta != null){
ta.rollback();
}
}finally{
//关闭session, user变为detached离线对象
HibernateUtil.closeSession(session);
}

}

================================================

关于inverse属性
inverse主要用在一对多和多对多双向关联上,inverse可以被设置到集合标签<set>上,
默认inverse为false,所以我们可以从”一“一端和”多“一端维护关联关系,
如果设置成inverse为true,则我们只能从多一端来维护关联关系

注意:inverse属性,只影响数据的存储,也就是持久化

<set name="students" inverse="true"> //inverse为true,则我们只能从多一端来维护关联关系
<key column="class_id" />
<one-to-many class="com.zd.model.Student" />
</set>

inverse和cascade
* inverse是关联关系的控制方向
* cascade操作上的连锁反应

Hibernate之中inverse与cascade的异同
2009-05-22 11:23

 

1、到底在哪用cascade="..."?

cascade属性并 不是多对多关系一定要用的,有了它只是让我们在插入或删除对像时更方便一些,只要在cascade的源头上插入或是删除,所有 cascade的关系就会被自己动的插入或是删除。便是为了能正确的cascade,unsaved-value是个很重要的属性。Hibernate通 过这个属性来判断一个对象应该save还是update,如果这个对象的id是unsaved-value的话,那说明这个对象不是 persistence object要save(insert);如果id是非unsaved-value的话,那说明这个对象是persistence object(数据库中已存在),只要update就行了。saveOrUpdate方法用的也是这个机制。

2、到底在哪用inverse="ture"?
set的inverse属性决定是否把对set的改动反映到数据库中去。inverse=false————反映;inverse=true————不反映”inverse属性默认为false

inverse 属性默认是false的,就是说关系的两端都来维护关系。这个意思就是说,如有一个Student, Teacher和TeacherStudent表,Student和Teacher是多对多对多关系,这个关系由TeacherStudent这个表来表 现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢?在用hibernate时,我们不会显示的对 TeacherStudent表做操作。对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看hbm文件中指 定的是"谁"维护关系,那个在插入或删除"谁"时,就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了,就是说关系另一头的对象已经set 或是add到"谁"这个对象里来了。前面说过inverse默认是false,就是关系的两端都维护关系,对其中任一个操作都会处发对表系表的操作。当在 关系的一头,如Student中的bag或set中用了inverse="true"时,那就代表关系是由另一关维护的(Teacher)。就是说当这插 入Student时,不会操作TeacherStudent表,即使Student已经知道了关系。只有当Teacher插入或删除时才会处发对关系表的 操作。所以,当关系的两头都用inverse="true"是不对的,就会导致任何操作都不处发对关系表的操作。当两端都是inverse= "false"或是default值是,在代码对关系显示的维护也是不对的,会导致在关系表中插入两次关系。

在一对多关系中 inverse就更有意义了。在多对多中,在哪端inverse="true"效果差不多(在效率上)。但是在一对多中,如果要一方维护关 系,就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。而如果让"多"方面维护关系时就不会有update 操作,因为关系就是在多方的对象中的,直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管 怎样说,还是让"多"方维护关系更直观一些。

(1)对one-to-many而言,改变set,会让hibernate执行一系列的update语句, 不会delete/insert数据
(2)对many-to-many而言,改变set,只修改关系表的数据,不会影响many-to-many的另一方。
(3)虽然one-to-many和many-to-many的数据库操作不一样,但目的都是一个:维护数据的一致性。

3、cascade和inverse有什么区别?
可以这样理解,cascade定义的是关系两端对象到对象的级联关系;而inverse定义的是关系和对象的级联关系。
inverse只对set+one-to-many(或many-to-many)有效,对many-to-one, one-to-one无效。cascade对关系标记都有效。

inverse对集合对象整体起作用,cascade对集合对象中的一个一个元素起作用,如果集合为空,那么cascade不会引发关联操作。
比如将集合对象置为null, school.setStudentSet(null)
inverse导致hibernate执行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?
cascade则不会执行对STUDENT表的关联更新, 因为集合中没有元素。
再比新增一个school, session.save(school)
inverse导致hibernate执行:
for( 对(school的每一个student ){
udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //将学生的school_id改为新的school的id
}
cascade导致hibernate执行:
for( 对school的每一个student ){
session.save(aStudent); //对学生执行save操作
}
extends:如果改变集合中的部分元素(比如新增一个元素),
inverse: hibernate先判断哪些元素改变了,对改变的元素执行相应的sql
cascade: 它总是对集合中的每个元素执行关联操作。
(在关联操作中,hibernate会判断操作的对象是否改变)
两个起作用的时机不同:
cascade:在对主控方操作时,级联发生。
inverse: 在flush时(commit会自动执行flush),对session中的所有set,hibernate判断每个set是否有变化,
对有变化的set执行相应的sql,执行之前,会有个判断:if( inverse == true ) return;可以看出cascade在先,inverse在后。
inverse 对set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。
对one-to-many,hibernate对many方的数据库表执行update语句。
对many-to-many, hibernate对关系表执行insert/update/delte语句,注意不是对many方的数据库表而是关系表。
cascase 对set都是一致的,不管one-to-many还是many-to-many。都简单地把操作传递到set中的每个元素。所以它总是更新many方的数据库表。

 

inverse是<set>的属性,所以只能在one-to-many方设置.拿常用的例子来说custom为one,而order为many,当两者相互关联,如果inverse设为false,当order有变化的时候,首先按照order的变化对order表进行变更,而后custom发现order有变化,也会按照order的变化对order表进行变更。当inverse设为true时,若order发生变化,则只是按照order进行更新,而custome方不会再对order表进行变更.就是这么个区别

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics