1. 程式人生 > >Hibernate單向多對一級聯刪除引發的問題

Hibernate單向多對一級聯刪除引發的問題

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,如果想實現在“一”刪除後,“多”的資料刪除,則必須配置一對多關係。由於需求要求當查詢職位時,同時返回部門,所以配置了多對一的關係。綜合起來,就是多對一(一對多)雙向關聯。