首先hibernate级联删除的前提是,首先需要在映射文件中配置,配置多表之间的关联关系:
下面以部门表(Dept)和员工表(Emp)为例:
1.在Emp.hbm.xml映射文件中配置many-to-one关系
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 4 <hibernate-mapping> 5 <class dynamic-update="true" name="entity.Emp" schema="ACCP" table="EMP"> 6 <id column="EMPNO" name="empNo" type="java.lang.String" length="20"> 7 <generator class="assigned"></generator> 8 </id> 9 <property name="empName" column="EMPNAME" type="java.lang.String" not-null="true"/> 10 <many-to-one name="dept" class="entity.Dept"> 11 <column name="DEPTNO" /> 12 </many-to-one> 13 </class> 14 </hibernate-mapping>
2.在Dept.hbm.xml映射文件中配置one-to-many关系
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 4 <hibernate-mapping> 5 <class dynamic-update="true" name="entity.Dept" schema="ACCP" table="DEPT"> 6 <id column="DEPTNO" name="deptNo" type="java.lang.String" length="20"> 7 <generator class="assigned"></generator> 8 </id> 9 <property name="deptName" column="DEPTNAME" type="java.lang.String" /> 10 <property name="location" column="LOCATION" type="java.lang.String" /> 11 <set name="emps" cascade="all"> 12 <key column="DEPTNO" /> 13 <one-to-many class="entity.Emp"/> 14 </set> 15 </class> 16 </hibernate-mapping>
3.编写部门和员工的实体类
1 package entity; 2 3 import java.io.Serializable; 4 5 public class Emp implements Serializable{ 6 /** 7 * 8 */ 9 private static final long serialVersionUID = 1L; 10 private String empNo; 11 private String empName; 12 private Dept dept; 13 14 public Emp(){} 15 16 public Emp(String empNo,String empName){ 17 this.empNo = empNo; 18 this.empName = empName; 19 } 20 public String getEmpNo() { 21 return empNo; 22 } 23 public void setEmpNo(String empNo) { 24 this.empNo = empNo; 25 } 26 public String getEmpName() { 27 return empName; 28 } 29 public void setEmpName(String empName) { 30 this.empName = empName; 31 } 32 33 public Dept getDept() { 34 return dept; 35 } 36 37 public void setDept(Dept dept) { 38 this.dept = dept; 39 } 40 41 }
1 package entity; 2 3 import java.io.Serializable; 4 import java.util.HashSet; 5 import java.util.Set; 6 7 public class Dept implements Serializable{ 8 /** 9 * 10 */ 11 private static final long serialVersionUID = 1L; 12 13 private String deptNo; 14 private String deptName; 15 private String location; 16 17 private Set<Emp> emps = new HashSet<Emp>(); 18 19 public Dept(){} 20 public Dept(String deptNo,String deptName,String location){ 21 this.deptNo = deptNo; 22 this.deptName = deptName; 23 this.location = location; 24 } 25 public String getDeptNo() { 26 return deptNo; 27 } 28 public void setDeptNo(String deptNo) { 29 this.deptNo = deptNo; 30 } 31 public String getDeptName() { 32 return deptName; 33 } 34 public void setDeptName(String deptName) { 35 this.deptName = deptName; 36 } 37 public String getLocation() { 38 return location; 39 } 40 public void setLocation(String location) { 41 this.location = location; 42 } 43 public Set<Emp> getEmps() { 44 return emps; 45 } 46 public void setEmps(Set<Emp> emps) { 47 this.emps = emps; 48 } 49 50 51 }
4.测试类
下面只写出测试的方法,
1 public void deleteDeptAndEmps(){ 2 currentSession(); 3 beginTransaction(); 4 Dept dept = (Dept)session.load(Dept.class, "1001"); 5 session.delete(dept); 6 commitTransaction(); 7 closeSession(); 8 }
5.进行测试
package test.dao; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Test; import dao.impl.EmpDaoImpl; public class EmpDaoImplTest extends EmpDaoImpl{ @Test public void testDeleteDeptAndEmps(){ deleteDeptAndEmps(); } }
运行的结果会出现下面错误:
1 org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update 2 at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:126) 3 at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:114) 4 at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) 5 at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275) 6 at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266) 7 at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:169) 8 at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321) 9 at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50) 10 at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1028) 11 at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:366) 12 at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137) 13 at util.HibernateUtil.commitTransaction(HibernateUtil.java:44) 14 at dao.impl.EmpDaoImpl.deleteDeptAndEmps(EmpDaoImpl.java:49) 15 at test.dao.EmpDaoImplTest.testDeleteDeptAndEmps(EmpDaoImplTest.java:21) 16 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 17 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 18 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 19 at java.lang.reflect.Method.invoke(Method.java:597) 20 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) 21 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) 22 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) 23 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) 24 at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79) 25 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71) 26 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) 27 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) 28 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) 29 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) 30 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) 31 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) 32 at org.junit.runners.ParentRunner.run(ParentRunner.java:236) 33 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 34 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 35 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 36 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) 37 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) 38 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 39 Caused by: java.sql.BatchUpdateException: ORA-01407: 无法更新 ("ACCP"."EMP"."DEPTNO") 为 NULL 40 41 at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:343) 42 at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10720) 43 at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70) 44 at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268) 45 ... 33 more
重点关注这一条错误:无法更新 ("ACCP"."EMP"."DEPTNO") 为 NULL
这时为什么呢,我们先看看执行hibernate删除时的sql语句:
Hibernate: select dept0_.DEPTNO as DEPTNO4_0_, dept0_.DEPTNAME as DEPTNAME4_0_, dept0_.LOCATION as LOCATION4_0_ from ACCP.DEPT dept0_ where dept0_.DEPTNO=? Hibernate: select emps0_.DEPTNO as DEPTNO1_, emps0_.EMPNO as EMPNO1_, emps0_.EMPNO as EMPNO5_0_, emps0_.EMPNAME as EMPNAME5_0_, emps0_.DEPTNO as DEPTNO5_0_ from ACCP.EMP emps0_ where emps0_.DEPTNO=? Hibernate: update ACCP.EMP set DEPTNO=null where DEPTNO=?
从上面我们可以看出,要执行级联删除,要删除Dept表中部门的同时删除Emp表对于部门下的所有员工信息,hibernate是这么干的:
(1)hibernate首先会查询出对应要删除的部门信息
(2)然后级联查询出对于的部门下的所有员工信息
(3)接着更新对于部门下的所有员工,将其部门更新为null
好,问题出现了,报错就在这一个步骤,既然要更新为null,那么Emp表在设计时就要能够让Emp表中的deptNo这个字段能够为null,也就是可以为空,下面就将正确的表
设计显示如下:
好了,上面的勾打上了,那么问题就解决了。
下面我们再执行一次级联删除的方法,看看最终效果:
对于的sql语句情况如下:
Hibernate: select dept0_.DEPTNO as DEPTNO4_0_, dept0_.DEPTNAME as DEPTNAME4_0_, dept0_.LOCATION as LOCATION4_0_ from ACCP.DEPT dept0_ where dept0_.DEPTNO=? Hibernate: select emps0_.DEPTNO as DEPTNO1_, emps0_.EMPNO as EMPNO1_, emps0_.EMPNO as EMPNO5_0_, emps0_.EMPNAME as EMPNAME5_0_, emps0_.DEPTNO as DEPTNO5_0_ from ACCP.EMP emps0_ where emps0_.DEPTNO=? Hibernate: ---------------------------------------------〈〈〈〈看该句下面的变化 update ACCP.EMP set DEPTNO=null where DEPTNO=? Hibernate: delete from ACCP.EMP where EMPNO=? Hibernate: delete from ACCP.DEPT where DEPTNO=?
从上面可以得出hibernate执行级联删除的步骤分为5步:
(1)同上
(2)同上
(3)同上
(4)删除Emp表中对应部门的所有信息记录
(5)最后删除Dept表中对应得部门信息
OK,问题解决,通过这个级联删除,使我们能够更加充分的认识hibernate作为持久化工具在处理持久化删除时它的操作方式,是不是很有意思。有问题的话大家可以一起交流。