在实际程序开发中,经常需要持久化临时对象,比如新建一个学生,将加入到一个存在的班级中(或者新建的一个班级),在这里不妨把引起级联操作的对象称为根对象(本文中所指的就是team对象) ,而根对象可能出于transient,persistence,detach三态,下面分别讨论
首先,建立数据库表:
CREATE
TABLE
certificate (
id
varchar
(
100
)
NOT
NULL
default
''
,
description
varchar
(
100
)
default
''
,
PRIMARY
KEY
(id)
);
CREATE
TABLE
student (
team_id
varchar
(
100
)
default
''
,
id
varchar
(
100
)
NOT
NULL
default
''
,
name
varchar
(
20
)
default
''
,
cardId
varchar
(
20
)
NOT
NULL
default
''
,
age
int
(
11
)
default
'
0
'
,
PRIMARY
KEY
(id)
);
CREATE
TABLE
team (
id
varchar
(
100
)
NOT
NULL
default
''
,
teamName
varchar
(
100
)
default
''
,
PRIMARY
KEY
(id)
);
INSERT
INTO
certificate
VALUES
(
'
1
'
,
'
110108
'
);
INSERT
INTO
certificate
VALUES
(
'
2
'
,
'
110109
'
);
INSERT
INTO
student
VALUES
(
'
1
'
,
'
1
'
,
'
tomclus
'
,
'
2006m
'
,
33
);
INSERT
INTO
student
VALUES
(
'
2
'
,
'
2
'
,
'
tom
'
,
'
2007m
'
,
22
);
INSERT
INTO
team
VALUES
(
'
1
'
,
'
team1
'
);
持久化对象Pojo
package
Cascade.Relation;
public
class
Certificate
...
{
private String id;
private String description;
private Student stu;
public Student getStu() ...{
return stu;
}
public void setStu(Student stu) ...{
this.stu = stu;
}
public String getDescription() ...{
return description;
}
public void setDescription(String description) ...{
this.description = description;
}
public String getId() ...{
return id;
}
public void setId(String id) ...{
this.id = id;
}
}
package
Cascade.Relation;
public
class
Student
...
{
private String id; //标识id
private String cardId; //学号
private String name; //学生姓名
private int age; //岁数
private Certificate cer;//身分证
private Team team;//班级
public int getAge() ...{
return age;
}
public String getName() ...{
return name;
}
public String getCardId() ...{
return cardId;
}
public void setId(String id) ...{
this.id = id;
}
public void setAge(int age) ...{
this.age = age;
}
public void setName(String stuName) ...{
this.name = stuName;
}
public void setCardId(String cardId) ...{
this.cardId = cardId;
}
public String getId() ...{
return id;
}
public Student() ...{ //无参的构造函数
}
public Certificate getCer() ...{
return cer;
}
public void setCer(Certificate pass) ...{
this.cer = pass;
}
public Team getTeam() ...{
return team;
}
public void setTeam(Team team) ...{
this.team = team;
}
}
package
Cascade.Relation;
import
java.util.HashSet;
import
java.util.Set;
public
class
Team
...
{
private String id;
private Set students=new HashSet();
private String teamName;
private Set tests;
public Set getTests() ...{
return tests;
}
public void setTests(Set tests) ...{
this.tests = tests;
}
public String getId() ...{
return id;
}
public void setId(String id) ...{
this.id = id;
}
public String getTeamName() ...{
return teamName;
}
public void setTeamName(String name) ...{
this.teamName = name;
}
public Set getStudents() ...{
return students;
}
public void setStudents(Set students) ...{
this.students = students;
}
}
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"
>
<!--
Generated by MyEclipse Hibernate Tools.
-->
<
hibernate-configuration
>
<
session-factory
>
<
property
name
="connection.username"
>
root
</
property
>
<
property
name
="connection.url"
>
jdbc:mysql://localhost:3306/schoolproject?characterEncoding=gb2312
&
useUnicode=true
</
property
>
<
property
name
="dialect"
>
org.hibernate.dialect.MySQLDialect
</
property
>
<
property
name
="myeclipse.connection.profile"
>
mysql
</
property
>
<
property
name
="connection.password"
>
1234
</
property
>
<
property
name
="connection.driver_class"
>
com.mysql.jdbc.Driver
</
property
>
<
property
name
="hibernate.dialect"
>
org.hibernate.dialect.MySQLDialect
</
property
>
<
property
name
="hibernate.show_sql"
>
true
</
property
>
<
property
name
="current_session_context_class"
>
thread
</
property
>
<
mapping
resource
="Cascade/Relation/Certificate.hbm.xml"
/>
<
mapping
resource
="Cascade/Relation/Student.hbm.xml"
/>
<
mapping
resource
="Cascade/Relation/Team.hbm.xml"
/>
</
session-factory
>
</
hibernate-configuration
>
HBM配置文件,其中Team到Student是cascade="save-update",Student到Certificate是cascade="all"
Team.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"
>
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<
hibernate-mapping
package
="Cascade.Relation"
>
<
class
name
="Cascade.Relation.Team"
table
="team"
lazy
="true"
>
<
id
name
="id"
column
="id"
>
<
generator
class
="uuid.hex"
></
generator
>
</
id
>
<
property
name
="teamName"
column
="teamName"
></
property
>
<
set
name
="students"
lazy
="true"
cascade
="save-update"
inverse
="false"
>
<
key
column
="team_id"
></
key
>
<
one-to-many
class
="Cascade.Relation.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"
>
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<
hibernate-mapping
package
="Cascade.Relation"
>
<
class
name
="Cascade.Relation.Student"
table
="student"
lazy
="true"
>
<
id
name
="id"
column
="id"
unsaved-value
="null"
>
<
generator
class
="uuid.hex"
></
generator
>
</
id
>
<
property
name
="cardId"
column
="cardId"
></
property
>
<
property
name
="name"
column
="name"
></
property
>
<
property
name
="age"
column
="age"
></
property
>
<!--
推荐1对多,多对多使用延迟加载,而多1对1,多对1采用预先抓取
-->
<
one-to-one
name
="cer"
class
="Cascade.Relation.Certificate"
constrained
="true"
fetch
="join"
cascade
="all"
>
</
one-to-one
>
<!--
推荐1对多,多对多使用延迟加载,而多1对1,多对1采用预先抓取
-->
<
many-to-one
name
="team"
column
="team_id"
fetch
="select"
cascade
="none"
class
="Cascade.Relation.Team"
></
many-to-one
>
</
class
>
</
hibernate-mapping
>
Certificate.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"
>
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<
hibernate-mapping
package
="Cascade.Relation"
>
<
class
name
="Cascade.Relation.Certificate"
table
="certificate"
lazy
="false"
>
<!--
共享主键关联,在子方配置constrained=true
-->
<
id
name
="id"
column
="id"
>
<
generator
class
="foreign"
>
<
param
name
="property"
>
stu
</
param
>
</
generator
>
</
id
>
<
property
name
="description"
column
="description"
></
property
>
<!--
推荐1对多,多对多使用延迟加载,而多1对1,多对1采用预先抓取
-->
<
one-to-one
name
="stu"
class
="Cascade.Relation.Student"
fetch
="join"
cascade
="none"
constrained
="true"
>
</
one-to-one
>
</
class
>
</
hibernate-mapping
>
(1)根对象为瞬态
测试程序:
package
Cascade.Relation;
import
java.io.File;
import
java.io.FileInputStream;
import
org.hibernate.Session;
import
org.hibernate.SessionFactory;
import
org.hibernate.Transaction;
import
org.hibernate.cfg.Configuration;
public
class
TestTransient
...
{
public static void main(String[] args) ...{
String filePath=System.getProperty("user.dir")+File.separator+"src/Cascade/Relation"+File.separator+"hibernate.cfg.xml";
File file=new File(filePath);
SessionFactory sessionFactory=new Configuration().configure(file).buildSessionFactory();
Session session=sessionFactory.openSession();
Transaction t=session.beginTransaction();
/**//*
* 发起级联的team为瞬态,team对student级联更新,student对certificate级联更新
*/
//新建学生
Student stu=new Student();
stu.setName("gao");
stu.setCardId("12345678");
stu.setAge(27);
//新建班级
Team team=new Team();
team.setTeamName("981");
//新建身份证
Certificate cer=new Certificate();
cer.setDescription("idcard");
//设置team和student的关联关系
stu.setTeam(team);
team.getStudents().add(stu);
//设置Student和Certificate的关联关系
stu.setCer(cer);
cer.setStu(stu);
session.save(team);
t.commit();
}
}
结果:
Hibernate: insert into team (teamName, id) values (?, ?)
Hibernate: insert into certificate (description, id) values (?, ?)
Hibernate: insert into student (cardId, name, age, team_id, id) values (?, ?, ?, ?, ?)
可以看到,保存team同时,也保存的student及certificte
(2)根对象为持久态
测试程序:
package
Cascade.Relation;
import
java.io.File;
import
java.io.FileInputStream;
import
org.hibernate.Session;
import
org.hibernate.SessionFactory;
import
org.hibernate.Transaction;
import
org.hibernate.cfg.Configuration;
public
class
TestPersistence
...
{
public static void main(String[] args) ...{
String filePath=System.getProperty("user.dir")+File.separator+"src/Cascade/Relation"+File.separator+"hibernate.cfg.xml";
File file=new File(filePath);
SessionFactory sessionFactory=new Configuration().configure(file).buildSessionFactory();
Session session=sessionFactory.openSession();
Transaction t=session.beginTransaction();
/**//*
* 发起级联的team为持久态,team对student级联更新,student对certificate级联更新
*/
//新建学生
Student stu=new Student();
stu.setName("gao");
stu.setCardId("12345678");
stu.setAge(27);
//新建身份证
Certificate cer=new Certificate();
cer.setDescription("idcard");
//设置Student和Certificate的关联关系
stu.setCer(cer);
cer.setStu(stu);
//读取Team
Team team=(Team)session.get(Team.class, "1");
team.getStudents().add(stu);//设置team和student关系
session.save(team);
t.commit();
}
}
结果:
Hibernate: select team0_.id as id2_0_, team0_.teamName as teamName2_0_ from team team0_ where team0_.id=?
Hibernate: select students0_.team_id as team5_3_, students0_.id as id3_, students0_.id as id1_2_, students0_.cardId as cardId1_2_, students0_.name as name1_2_, students0_.age as age1_2_, students0_.team_id as team5_1_2_, certificat1_.id as id0_0_, certificat1_.description as descript2_0_0_, student2_.id as id1_1_, student2_.cardId as cardId1_1_, student2_.name as name1_1_, student2_.age as age1_1_, student2_.team_id as team5_1_1_ from student students0_ inner join certificate certificat1_ on students0_.id=certificat1_.id left outer join student student2_ on certificat1_.id=student2_.id where students0_.team_id=?
Hibernate: insert into certificate (description, id) values (?, ?)
Hibernate: insert into student (cardId, name, age, team_id, id) values (?, ?, ?, ?, ?)
可以看到,前两个sql是读取team及fetch,然后保存certificate和student
(3)根对象是游离态
测试程序:
package
Cascade.Relation;
import
java.io.File;
import
java.io.FileInputStream;
import
org.hibernate.Session;
import
org.hibernate.SessionFactory;
import
org.hibernate.Transaction;
import
org.hibernate.cfg.Configuration;
public
class
TestDetach
...
{
public static void main(String[] args) ...{
String filePath=System.getProperty("user.dir")+File.separator+"src/Cascade/Relation"+File.separator+"hibernate.cfg.xml";
File file=new File(filePath);
SessionFactory sessionFactory=new Configuration().configure(file).buildSessionFactory();
Session session=sessionFactory.openSession();
Session session2=sessionFactory.openSession();
Transaction t=session.beginTransaction();
/**//*
* 发起级联的team为游离态,team对student级联更新,student对certificate级联更新
*/
//新建学生
Student stu=new Student();
stu.setName("gao");
stu.setCardId("12345678");
stu.setAge(27);
//新建身份证
Certificate cer=new Certificate();
cer.setDescription("idcard");
//设置Student和Certificate的关联关系
stu.setCer(cer);
cer.setStu(stu);
//读取Team
Team team=(Team)session.get(Team.class, "1");
t.commit();
session.close();//读取完后关闭session,team变成游离态
Transaction t2=session2.beginTransaction();
team.getStudents().add(stu);//设置team和student关系
session2.update(team);//用另一个session重新激活team为持久太
t2.commit();
}
}
运行结果会出现异常如下:
xception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: Cascade.Relation.Team.students, no session or session was closed
这是因为,我们Team对Student是延迟加载,读取Team后,session关闭,重新把team通过另一个session2变成持久后,team.getStudent()是取不到任何东西的,因为其student集合没有得到初始化,所以我们企图用team.getStudent().add(student)的方式级联保存student是不行的,解决方式很简单,讲Team.hbm.xml中Team对Student的lazy=true 改成lazy=false 结果如下:
Hibernate: select team0_.id as id2_0_, team0_.teamName as teamName2_0_ from team team0_ where team0_.id=?
Hibernate: select students0_.team_id as team5_3_, students0_.id as id3_, students0_.id as id1_2_, students0_.cardId as cardId1_2_, students0_.name as name1_2_, students0_.age as age1_2_, students0_.team_id as team5_1_2_, certificat1_.id as id0_0_, certificat1_.description as descript2_0_0_, student2_.id as id1_1_, student2_.cardId as cardId1_1_, student2_.name as name1_1_, student2_.age as age1_1_, student2_.team_id as team5_1_1_ from student students0_ inner join certificate certificat1_ on students0_.id=certificat1_.id left outer join student student2_ on certificat1_.id=student2_.id where students0_.team_id=?
Hibernate: insert into certificate (description, id) values (?, ?)
Hibernate: insert into student (cardId, name, age, team_id, id) values (?, ?, ?, ?, ?)
Hibernate: update team set teamName=? where id=?
Hibernate: update student set cardId=?, name=?, age=?, team_id=? where id=?
Hibernate: update certificate set description=? where id=?
Hibernate: update student set team_id=? where id=?
主意红色的三条SQL,如果我们脱管根对象没有发生过任何改变,则这些update是没有意义的,我们可以在hbm配置文件中的class级别加上select-before-update="true" 在运行update之前先吧数据库中的数据和脱管对象进行比较,如果不同,再进行更新,如下:
Hibernate: select team0_.id as id2_0_, team0_.teamName as teamName2_0_ from team team0_ where team0_.id=?
Hibernate: select students0_.team_id as team5_3_, students0_.id as id3_, students0_.id as id1_2_, students0_.cardId as cardId1_2_, students0_.name as name1_2_, students0_.age as age1_2_, students0_.team_id as team5_1_2_, certificat1_.id as id0_0_, certificat1_.description as descript2_0_0_, student2_.id as id1_1_, student2_.cardId as cardId1_1_, student2_.name as name1_1_, student2_.age as age1_1_, student2_.team_id as team5_1_1_ from student students0_ inner join certificate certificat1_ on students0_.id=certificat1_.id left outer join student student2_ on certificat1_.id=student2_.id where students0_.team_id=?
Hibernate: select team_.id, team_.teamName as teamName2_ from team team_ where team_.id=?
Hibernate: select student_.id, student_.cardId as cardId1_, student_.name as name1_, student_.age as age1_, student_.team_id as team5_1_ from student student_ where student_.id=?
Hibernate: select certificat_.id, certificat_.description as descript2_0_ from certificate certificat_ where certificat_.id=?
Hibernate: insert into certificate (description, id) values (?, ?)
Hibernate: insert into student (cardId, name, age, team_id, id) values (?, ?, ?, ?, ?)
Hibernate: update student set team_id=? where id=?
这样会多出一些Select SQL,但相比update,效率要高很多