Hibernate级联操作Cascade学之---save-update
2007-12-04 14:50
666 查看
所在cascade,就是说我在更新一方的时候,可以根据这一方对象之间的关联关系,去对被关联方进行持久化,比如说Team和Student之间的1对多关系,使用cascade,可以在team方维护其持有的student集合时,自动对其新增,修改,删除的student对象进行持久化,而没必要显示的进行session.save(student)操作
数据库脚本:
create table student(id varchar(32) primary key,
team_id varchar(32),
name varchar(32),
cardid varchar(32),
age int);
create table team(id varchar(32) primary key,
team_id varchar(32),
teamname varchar(32));
insert into team values("1","1","team1");
insert into student values("1","1","stu1","20070101",22);
insert into student values("2","1","stu2","20070102",23);
持久化JavaBean
package Cascade.saveUpdate;
public class Student ...{
private String id;
private String cardid;
private String name;
private int age;
private Team team;
public String getId() ...{
return id;
}
public void setId(String id) ...{
this.id = id;
}
public String getCardid() ...{
return cardid;
}
public void setCardid(String cardid) ...{
this.cardid = cardid;
}
public String getName() ...{
return name;
}
public void setName(String name) ...{
this.name = name;
}
public int getAge() ...{
return age;
}
public void setAge(int age) ...{
this.age = age;
}
public Team getTeam() ...{
return team;
}
public void setTeam(Team team) ...{
this.team = team;
}
}
package Cascade.saveUpdate;
import java.util.HashSet;
import java.util.Set;
public class Team ...{
private String id;
private Set students=new HashSet();
private String teamName;
public String getId() ...{
return id;
}
public void setId(String id) ...{
this.id = id;
}
public Set getStudents() ...{
return students;
}
public void setStudents(Set students) ...{
this.students = students;
}
public String getTeamName() ...{
return teamName;
}
public void setTeamName(String teamName) ...{
this.teamName = teamName;
}
}
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>
<property name="jdbc.batch_size">15</property>
<mapping resource="Cascade/saveUpdate/Student.hbm.xml" />
<mapping resource="Cascade/saveUpdate/Team.hbm.xml" />
</session-factory>
</hibernate-configuration>
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>
<class name="Cascade.saveUpdate.Student" table="student">
<id name="id" unsaved-value="null">
<generator class="uuid.hex"></generator>
</id>
<property name="cardid" type="string"/>
<property name="name" type="string"/>
<property name="age" type="int"/>
<many-to-one name="team"
column="team_id"
class="Cascade.saveUpdate.Team"
lazy="no-proxy">
</many-to-one>
</class>
</hibernate-mapping>
Team.hbm.xml 这里先配置成cascade="none"
<?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>
<class name="Cascade.saveUpdate.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" inverse="false" cascade="none">
<key column="team_id"></key>
<one-to-many class="Cascade.saveUpdate.Student"/>
</set>
</class>
</hibernate-mapping>
测试代码:
package Cascade.saveUpdate;
import java.io.File;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Test ...{
public static void main(String[] args) ...{
String filePath=System.getProperty("user.dir")+File.separator+"src/Cascade/saveUpdate"+File.separator+"hibernate.cfg.xml";
File file=new File(filePath);
System.out.println(filePath);
SessionFactory sessionFactory=new Configuration().configure(file).buildSessionFactory();
Session session=sessionFactory.openSession();
Transaction t=session.beginTransaction();
Student newStu=new Student(); //建立一个student对象,瞬态
newStu.setCardid("12345");
newStu.setAge(22);
newStu.setName("newStu");
Team team=(Team)session.get(Team.class, "1");//获得team对象
Student removeStu=(Student)session.get(Student.class,"1");//获得需要删除的stu对象
team.getStudents().add(newStu); //增加student到team持有的student集合
team.getStudents().remove(removeStu); //删除student到team持有的student集合
t.commit();
}
}
运行代码,结果如下:
Hibernate: select team0_.id as id1_0_, team0_.teamname as teamname1_0_ from team team0_ where team0_.id=?
Hibernate: select student0_.id as id0_0_, student0_.cardid as cardid0_0_, student0_.name as name0_0_, student0_.age as age0_0_, student0_.team_id as team5_0_0_ from student student0_ where student0_.id=?
Hibernate: select students0_.team_id as team5_1_, students0_.id as id1_, students0_.id as id0_0_, students0_.cardid as cardid0_0_, students0_.name as name0_0_, students0_.age as age0_0_, students0_.team_id as team5_0_0_ from student students0_ where students0_.team_id=?
Hibernate: update student set team_id=null where team_id=? and id=?
Hibernate: update student set team_id=? where id=?
Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Cascade.saveUpdate.Student
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:78)
at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:755)
at org.hibernate.persister.collection.AbstractCollectionPersister.insertRows(AbstractCollectionPersister.java:1364)
at org.hibernate.action.CollectionUpdateAction.execute(CollectionUpdateAction.java:56)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at Cascade.saveUpdate.Test.main(Test.java:30)
可以看到红色的异常,提示没有对象引用,是因为我们没有显示的调用session.save(newStu)又没有设置team端的cascade
如果代码改成session.save(newStu); team.getStudents().add(newStu)就不会有任何问题了
下面,我们看下如果运用cascade="save-update"修正上面的错误,首先修改Team.hbm.xml
注意:cascade="saveupdate"和inverse="false"必须同时设置,才能让team维护级联关系,如果inverse="true"(不让Team维护关系,哪cascad设置也不会有任何作用,虽然可以新插入一条学生的记录,但其team_id为null,无法实现引用关系),如下图数据库表:
<?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>
<class name="Cascade.saveUpdate.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" inverse="false" cascade="save-update">
<key column="team_id"></key>
<one-to-many class="Cascade.saveUpdate.Student"/>
</set>
</class>
</hibernate-mapping>
重新运行代码:
Hibernate: select team0_.id as id1_0_, team0_.teamname as teamname1_0_ from team team0_ where team0_.id=?
Hibernate: select student0_.id as id0_0_, student0_.cardid as cardid0_0_, student0_.name as name0_0_, student0_.age as age0_0_, student0_.team_id as team5_0_0_ from student student0_ where student0_.id=?
Hibernate: select students0_.team_id as team5_1_, students0_.id as id1_, students0_.id as id0_0_, students0_.cardid as cardid0_0_, students0_.name as name0_0_, students0_.age as age0_0_, students0_.team_id as team5_0_0_ from student students0_ where students0_.team_id=?
Hibernate: insert into student (cardid, name, age, team_id, id) values (?, ?, ?, ?, ?)
Hibernate: update student set team_id=null where team_id=? and id=?
Hibernate: update student set team_id=? where id=?
可以看到,红色的部分插入了新数据,并使用update更新其team_id字段,维护约束关系
不过,需要主意的是,使用cascade="save-update"删除集合中的数据时候,不能真正的从数据库中删除记录,只是简单的讲其关联约束字段(如team_id)设置为null,被删除的对象还是保留在session缓存中,还是可以通过
removStu.setName("111")改变对象并进行持久化,如果需要使用这种方式删除对象并持久化,可以设置
cascade="all-delete-orphan"
数据库脚本:
create table student(id varchar(32) primary key,
team_id varchar(32),
name varchar(32),
cardid varchar(32),
age int);
create table team(id varchar(32) primary key,
team_id varchar(32),
teamname varchar(32));
insert into team values("1","1","team1");
insert into student values("1","1","stu1","20070101",22);
insert into student values("2","1","stu2","20070102",23);
持久化JavaBean
package Cascade.saveUpdate;
public class Student ...{
private String id;
private String cardid;
private String name;
private int age;
private Team team;
public String getId() ...{
return id;
}
public void setId(String id) ...{
this.id = id;
}
public String getCardid() ...{
return cardid;
}
public void setCardid(String cardid) ...{
this.cardid = cardid;
}
public String getName() ...{
return name;
}
public void setName(String name) ...{
this.name = name;
}
public int getAge() ...{
return age;
}
public void setAge(int age) ...{
this.age = age;
}
public Team getTeam() ...{
return team;
}
public void setTeam(Team team) ...{
this.team = team;
}
}
package Cascade.saveUpdate;
import java.util.HashSet;
import java.util.Set;
public class Team ...{
private String id;
private Set students=new HashSet();
private String teamName;
public String getId() ...{
return id;
}
public void setId(String id) ...{
this.id = id;
}
public Set getStudents() ...{
return students;
}
public void setStudents(Set students) ...{
this.students = students;
}
public String getTeamName() ...{
return teamName;
}
public void setTeamName(String teamName) ...{
this.teamName = teamName;
}
}
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>
<property name="jdbc.batch_size">15</property>
<mapping resource="Cascade/saveUpdate/Student.hbm.xml" />
<mapping resource="Cascade/saveUpdate/Team.hbm.xml" />
</session-factory>
</hibernate-configuration>
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>
<class name="Cascade.saveUpdate.Student" table="student">
<id name="id" unsaved-value="null">
<generator class="uuid.hex"></generator>
</id>
<property name="cardid" type="string"/>
<property name="name" type="string"/>
<property name="age" type="int"/>
<many-to-one name="team"
column="team_id"
class="Cascade.saveUpdate.Team"
lazy="no-proxy">
</many-to-one>
</class>
</hibernate-mapping>
Team.hbm.xml 这里先配置成cascade="none"
<?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>
<class name="Cascade.saveUpdate.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" inverse="false" cascade="none">
<key column="team_id"></key>
<one-to-many class="Cascade.saveUpdate.Student"/>
</set>
</class>
</hibernate-mapping>
测试代码:
package Cascade.saveUpdate;
import java.io.File;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Test ...{
public static void main(String[] args) ...{
String filePath=System.getProperty("user.dir")+File.separator+"src/Cascade/saveUpdate"+File.separator+"hibernate.cfg.xml";
File file=new File(filePath);
System.out.println(filePath);
SessionFactory sessionFactory=new Configuration().configure(file).buildSessionFactory();
Session session=sessionFactory.openSession();
Transaction t=session.beginTransaction();
Student newStu=new Student(); //建立一个student对象,瞬态
newStu.setCardid("12345");
newStu.setAge(22);
newStu.setName("newStu");
Team team=(Team)session.get(Team.class, "1");//获得team对象
Student removeStu=(Student)session.get(Student.class,"1");//获得需要删除的stu对象
team.getStudents().add(newStu); //增加student到team持有的student集合
team.getStudents().remove(removeStu); //删除student到team持有的student集合
t.commit();
}
}
运行代码,结果如下:
Hibernate: select team0_.id as id1_0_, team0_.teamname as teamname1_0_ from team team0_ where team0_.id=?
Hibernate: select student0_.id as id0_0_, student0_.cardid as cardid0_0_, student0_.name as name0_0_, student0_.age as age0_0_, student0_.team_id as team5_0_0_ from student student0_ where student0_.id=?
Hibernate: select students0_.team_id as team5_1_, students0_.id as id1_, students0_.id as id0_0_, students0_.cardid as cardid0_0_, students0_.name as name0_0_, students0_.age as age0_0_, students0_.team_id as team5_0_0_ from student students0_ where students0_.team_id=?
Hibernate: update student set team_id=null where team_id=? and id=?
Hibernate: update student set team_id=? where id=?
Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Cascade.saveUpdate.Student
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:78)
at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:755)
at org.hibernate.persister.collection.AbstractCollectionPersister.insertRows(AbstractCollectionPersister.java:1364)
at org.hibernate.action.CollectionUpdateAction.execute(CollectionUpdateAction.java:56)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at Cascade.saveUpdate.Test.main(Test.java:30)
可以看到红色的异常,提示没有对象引用,是因为我们没有显示的调用session.save(newStu)又没有设置team端的cascade
如果代码改成session.save(newStu); team.getStudents().add(newStu)就不会有任何问题了
下面,我们看下如果运用cascade="save-update"修正上面的错误,首先修改Team.hbm.xml
注意:cascade="saveupdate"和inverse="false"必须同时设置,才能让team维护级联关系,如果inverse="true"(不让Team维护关系,哪cascad设置也不会有任何作用,虽然可以新插入一条学生的记录,但其team_id为null,无法实现引用关系),如下图数据库表:
<?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>
<class name="Cascade.saveUpdate.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" inverse="false" cascade="save-update">
<key column="team_id"></key>
<one-to-many class="Cascade.saveUpdate.Student"/>
</set>
</class>
</hibernate-mapping>
重新运行代码:
Hibernate: select team0_.id as id1_0_, team0_.teamname as teamname1_0_ from team team0_ where team0_.id=?
Hibernate: select student0_.id as id0_0_, student0_.cardid as cardid0_0_, student0_.name as name0_0_, student0_.age as age0_0_, student0_.team_id as team5_0_0_ from student student0_ where student0_.id=?
Hibernate: select students0_.team_id as team5_1_, students0_.id as id1_, students0_.id as id0_0_, students0_.cardid as cardid0_0_, students0_.name as name0_0_, students0_.age as age0_0_, students0_.team_id as team5_0_0_ from student students0_ where students0_.team_id=?
Hibernate: insert into student (cardid, name, age, team_id, id) values (?, ?, ?, ?, ?)
Hibernate: update student set team_id=null where team_id=? and id=?
Hibernate: update student set team_id=? where id=?
可以看到,红色的部分插入了新数据,并使用update更新其team_id字段,维护约束关系
不过,需要主意的是,使用cascade="save-update"删除集合中的数据时候,不能真正的从数据库中删除记录,只是简单的讲其关联约束字段(如team_id)设置为null,被删除的对象还是保留在session缓存中,还是可以通过
removStu.setName("111")改变对象并进行持久化,如果需要使用这种方式删除对象并持久化,可以设置
cascade="all-delete-orphan"
相关文章推荐
- 解决Hibernate的session.save/update/delete操作无效问题(事务管理)
- Hibernate级联操作和加载机制(一) cascade and fetch
- 【SSH三大框架】Hibernate基础第九篇:cascade关联关系的级联操作
- hibernate的级联操作cascade
- Hibernate的Cascade——级联操作
- Hibernate的Cascade——级联操作
- Hibernate级联操作和加载机制(二) cascade and fetch
- 【SSH三大框架】Hibernate基础第九篇:cascade关联关系的级联操作
- 级联操作--on delete/update cascade
- Hibernate 级联操作cascade及inverse
- 【Hibernate】级联操作 cascade 选项
- Hibernate入门(五)hibernate的级联(cascade)表操作
- Hibernate Set Cascade Example (save, update, delete and delete-orphan)
- Hibernate入门(五)hibernate的级联(cascade)表操作
- hibernate中关系操作(inverse)和级联操作(cascade)详解
- Hibernate级联操作Cascade
- Hibernate中级联操作cascade选项
- Hibernate级联操作cascade
- Hibernate中级联操作cascade选项
- Hibernate入门(五)hibernate的级联(cascade)表操作