hibernate 级联删除报更新失败的问题(org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update)

首先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作为持久化工具在处理持久化删除时它的操作方式,是不是很有意思。有问题的话大家可以一起交流。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。