Hibernate單向多對一級聯刪除引發的問題
阿新 • • 發佈:2019-01-06
Hibernate單向多對一在級聯刪除時,會出現一些問題。
下面模擬我遇到的問題:
模擬場景:有一個部門表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,如果想實現在“一”刪除後,“多”的資料刪除,則必須配置一對多關係。由於需求要求當查詢職位時,同時返回部門,所以配置了多對一的關係。綜合起來,就是多對一(一對多)雙向關聯。