Hibernate单向多对一在级联删除时,会出现一些问题。
下面模拟我遇到的问题:
这次模拟与之前的一次模拟方法一直,博客:http://blog.csdn.net/openjdk8/article/details/38424403
模拟场景:有一个部门表t_dept,职位表t_position。
需求:当删除部门表时,不管职位表有没数据,照样删除。删除职位表就直接删除。
1,建表:
建表:
t_dept::部门表
t_position:职位表
CREATE TABLE t_dept( dept_id INT PRIMARY KEY auto_increment , #部门Id dept_name VARCHAR(45) #部门名字 ) ; CREATE TABLE t_position( position_id INT PRIMARY KEY auto_increment , #职位Id position_name VARCHAR(45) , #职位名字 dept_id INT , #外键 CONSTRAINT fk_dept_position FOREIGN KEY(dept_id) REFERENCES t_dept(dept_id) ) ;
2,实体类(采用单向多对一):
Dept.java
package org.jian.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "t_dept") public class Dept { private int id; private String name; @Id @GenericGenerator(name = "generator", strategy = "increment") @GeneratedValue(generator = "generator") @Column(name="dept_id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name="dept_name") public String getName() { return name; } public void setName(String name) { this.name = name; } }
Position.java
package org.jian.domain; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "t_position") public class Position { private int id; private String name; private Dept dept; @Id @GenericGenerator(name = "generator", strategy = "increment") @GeneratedValue(generator = "generator") @Column(name="position_id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name="position_name") public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToOne(targetEntity = Dept.class, fetch = FetchType.LAZY, cascade = { CascadeType.ALL }) @JoinColumn(name = "dept_id") public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; } }
3,测试类:
package org.jian.domain; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.AnnotationConfiguration; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class DeptTest { private static SessionFactory sf ; @BeforeClass public static void beforeClass(){ sf = new AnnotationConfiguration().configure().buildSessionFactory() ; } @AfterClass public static void afterClass(){ sf.close(); } /** * 系统初始化,添加数据 * 软件部:架构师,软件工程师,程序员 * 财务部:会计 * 人事部:无数据 */ @Test public void testInit(){ Session session = sf.openSession() ; Transaction tx = session.beginTransaction() ; Dept d1 = new Dept() ; d1.setName("软件部"); Dept d2 = new Dept() ; d2.setName("财务部"); Dept d3 = new Dept() ; d3.setName("人事部"); Position p1 = new Position() ; p1.setName("架构师"); p1.setDept(d1); Position p2 = new Position() ; p2.setName("软件工程师"); p2.setDept(d1); Position p3 = new Position() ; p3.setName("程序员"); p3.setDept(d1); Position p4 = new Position() ; p4.setName("会计"); p4.setDept(d2); session.save(d1) ; session.save(d2) ; session.save(d3) ; session.save(p1) ; session.save(p2) ; session.save(p3) ; session.save(p4) ; tx.commit(); session.close() ; } /** * 我们尝试删除有数据的软件部 */ @Test public void testDelDept1(){ Session session = sf.openSession() ; Transaction tx = session.beginTransaction() ; Dept dept = (Dept)session.get(Dept.class, 1) ; session.delete(dept); tx.commit(); session.close() ; } /** * 测试只删除职位(程序员)不删除部门 */ @Test public void testDelDept2(){ Session session = sf.openSession() ; Transaction tx = session.beginTransaction() ; Position position = (Position)session.get(Position.class, 3) ;//程序员ID为3 session.delete(position); tx.commit(); session.close() ; } }
测试第一个方法,看到绿条,控制台输出:
Hibernate: select max(dept_id) from t_dept Hibernate: select max(position_id) from t_position Hibernate: insert into t_dept (dept_name, dept_id) values (?, ?) Hibernate: insert into t_dept (dept_name, dept_id) values (?, ?) Hibernate: insert into t_dept (dept_name, dept_id) values (?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?)
Mysql生成的表:
测试testDelDept1()方法,看到了一个恶心的红条,并且报错了,控制台输出和Junit如下所示:
Hibernate: select dept0_.dept_id as dept1_0_0_, dept0_.dept_name as dept2_0_0_ from t_dept dept0_ where dept0_.dept_id=? Hibernate: delete from t_dept where dept_id=?
测试testDelDept2()方法同样如此,
Hibernate: select position0_.position_id as position1_1_0_, position0_.dept_id as dept3_1_0_, position0_.position_name as position2_1_0_ from t_position position0_ where position0_.position_id=? Hibernate: select dept0_.dept_id as dept1_0_0_, dept0_.dept_name as dept2_0_0_ from t_dept dept0_ where dept0_.dept_id=? Hibernate: delete from t_position where position_id=? Hibernate: delete from t_dept where dept_id=?
当我把Position.java里面的:
@ManyToOne(targetEntity = Dept.class, fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
改为:
@ManyToOne(targetEntity = Dept.class, fetch = FetchType.LAZY)
时,没有报错,可以成功删除执行了下面语句。
Hibernate: select position0_.position_id as position1_1_0_, position0_.dept_id as dept3_1_0_, position0_.position_name as position2_1_0_ from t_position position0_ where position0_.position_id=? Hibernate: delete from t_position where position_id=?
从上面我们可以猜测,cascade配置在哪一方配置,那么那个实体类进行操作时,会执行级联的更新或者是删除。
为了解决这个问题,我上网查了很多资料,最后尝试了一个办法,那就是使用双向多对一来配置。
这样我们就需要改一下Dept.java类了:
package org.jian.domain; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "t_dept") public class Dept { private int id; private String name; private Set<Position> positions = new HashSet<Position>() ; @Id @GenericGenerator(name = "generator", strategy = "increment") @GeneratedValue(generator = "generator") @Column(name="dept_id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name="dept_name") public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(mappedBy = "dept", cascade = CascadeType.ALL, fetch = FetchType.LAZY) public Set<Position> getPositions() { return positions; } public void setPositions(Set<Position> positions) { this.positions = positions; } }
测试类改一下初始化的代码:
@Test public void testInit(){ Session session = sf.openSession() ; Transaction tx = session.beginTransaction() ; Dept d1 = new Dept() ; d1.setName("软件部"); Dept d2 = new Dept() ; d2.setName("财务部"); Dept d3 = new Dept() ; d3.setName("人事部"); Position p1 = new Position() ; p1.setName("架构师"); p1.setDept(d1); Position p2 = new Position() ; p2.setName("软件工程师"); p2.setDept(d1); Position p3 = new Position() ; p3.setName("程序员"); p3.setDept(d1); Position p4 = new Position() ; p4.setName("会计"); p4.setDept(d2); d1.getPositions().add(p1) ; d1.getPositions().add(p2) ; d1.getPositions().add(p3) ; d2.getPositions().add(p4) ; session.save(d1) ; session.save(d2) ; session.save(d3) ; session.save(p1) ; session.save(p2) ; session.save(p3) ; session.save(p4) ; tx.commit(); session.close() ; }
测试这个方法,控制台输出如下:
Hibernate: select max(dept_id) from t_dept Hibernate: select max(position_id) from t_position Hibernate: insert into t_dept (dept_name, dept_id) values (?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?) Hibernate: insert into t_dept (dept_name, dept_id) values (?, ?) Hibernate: insert into t_position (dept_id, position_name, position_id) values (?, ?, ?) Hibernate: insert into t_dept (dept_name, dept_id) values (?, ?)
执行testDelDept1()方法,看到了绿条
Hibernate: select dept0_.dept_id as dept1_0_0_, dept0_.dept_name as dept2_0_0_ from t_dept dept0_ where dept0_.dept_id=? Hibernate: select positions0_.dept_id as dept3_0_1_, positions0_.position_id as position1_1_, positions0_.position_id as position1_1_0_, positions0_.dept_id as dept3_1_0_, positions0_.position_name as position2_1_0_ from t_position positions0_ where positions0_.dept_id=? Hibernate: delete from t_position where position_id=? Hibernate: delete from t_position where position_id=? Hibernate: delete from t_position where position_id=? Hibernate: delete from t_dept where dept_id=?
在“一”的一方成功实现了级联删除。
重新生成表
测试testDelDept2()方法,看到了绿条,控制台输出:
Hibernate: select position0_.position_id as position1_1_0_, position0_.dept_id as dept3_1_0_, position0_.position_name as position2_1_0_ from t_position position0_ where position0_.position_id=? Hibernate: delete from t_position where position_id=?
~综:
1,在一对多(多对一)关系中,如果某个实体类配置了cascade属性(除了设置为‘‘none"),则这个实体类将会执行级联操作。
2,如果想实现在“一”删除后,“多”的数据删除,则必须配置一对多关系。由于需求要求当查询职位时,同时返回部门,所以配置了多对一的关系。综合起来,就是多对一(一对多)双向关联。
Hibernate单向多对一级联删除引发的问题