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

时间:2024-01-12 09:20:02

首先hibernate级联删除的前提是,首先需要在映射文件中配置,配置多表之间的关联关系:

下面以部门表(Dept)和员工表(Emp)为例:

1.在Emp.hbm.xml映射文件中配置many-to-one关系

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class dynamic-update="true" name="entity.Emp" schema="ACCP" table="EMP">
<id column="EMPNO" name="empNo" type="java.lang.String" length="20">
<generator class="assigned"></generator>
</id>
<property name="empName" column="EMPNAME" type="java.lang.String" not-null="true"/>
<many-to-one name="dept" class="entity.Dept">
<column name="DEPTNO" />
</many-to-one>
</class>
</hibernate-mapping>

2.在Dept.hbm.xml映射文件中配置one-to-many关系

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class dynamic-update="true" name="entity.Dept" schema="ACCP" table="DEPT">
<id column="DEPTNO" name="deptNo" type="java.lang.String" length="20">
<generator class="assigned"></generator>
</id>
<property name="deptName" column="DEPTNAME" type="java.lang.String" />
<property name="location" column="LOCATION" type="java.lang.String" />
<set name="emps" cascade="all">
<key column="DEPTNO" />
<one-to-many class="entity.Emp"/>
</set>
</class>
</hibernate-mapping>

3.编写部门和员工的实体类

 package entity;

 import java.io.Serializable;

 public class Emp implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String empNo;
private String empName;
private Dept dept; public Emp(){} public Emp(String empNo,String empName){
this.empNo = empNo;
this.empName = empName;
}
public String getEmpNo() {
return empNo;
}
public void setEmpNo(String empNo) {
this.empNo = empNo;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
} public Dept getDept() {
return dept;
} public void setDept(Dept dept) {
this.dept = dept;
} }
 package entity;

 import java.io.Serializable;
import java.util.HashSet;
import java.util.Set; public class Dept implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L; private String deptNo;
private String deptName;
private String location; private Set<Emp> emps = new HashSet<Emp>(); public Dept(){}
public Dept(String deptNo,String deptName,String location){
this.deptNo = deptNo;
this.deptName = deptName;
this.location = location;
}
public String getDeptNo() {
return deptNo;
}
public void setDeptNo(String deptNo) {
this.deptNo = deptNo;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Set<Emp> getEmps() {
return emps;
}
public void setEmps(Set<Emp> emps) {
this.emps = emps;
} }

4.测试类

下面只写出测试的方法,

 public void deleteDeptAndEmps(){
currentSession();
beginTransaction();
Dept dept = (Dept)session.load(Dept.class, "1001");
session.delete(dept);
commitTransaction();
closeSession();
}

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();
}
}

运行的结果会出现下面错误:

 org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:126)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:114)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:169)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1028)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:366)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at util.HibernateUtil.commitTransaction(HibernateUtil.java:44)
at dao.impl.EmpDaoImpl.deleteDeptAndEmps(EmpDaoImpl.java:49)
at test.dao.EmpDaoImplTest.testDeleteDeptAndEmps(EmpDaoImplTest.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.sql.BatchUpdateException: ORA-01407: 无法更新 ("ACCP"."EMP"."DEPTNO") 为 NULL at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:343)
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10720)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 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,也就是可以为空,下面就将正确的表

设计显示如下:

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

好了,上面的勾打上了,那么问题就解决了。

下面我们再执行一次级联删除的方法,看看最终效果:

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

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