1. 程式人生 > >JPA實體繼承的對映

JPA實體繼承的對映

注:文章中據說的實體指的是被@Entity註解的類。

JPA中物件關係對映通常情況下是一個實體對應一個表,兩個實體之間沒有任何關係。如果兩個實體之間是繼承關係,那麼該如何對映呢?


JPA中的實體支援繼承對映,多型關聯,多型查詢。抽象類和具體的類都可以是實體,且都可以使用@Entity來註解,對映成實體,並查詢封裝成一個實體。實體類可以繼承非實體類,非實體類也可以繼承實體類。

JPA的繼承對映有如下幾種情況:

一、實體類繼承抽象(具體)實體類
抽象類可以指定成為一個實體,抽象實體和具體實體的唯一區別只是抽象實體不能夠被直接例項化。抽象實體能夠被對映成一個實體並能夠作為查詢目標。
抽象實體類使用@Entity註解或在XML描述符表示成一個實體。

這種對映相對複雜,後面會專門寫一篇文章來舉例說明。這裡就不再多說。

二、實體類繼承對映超類(Mapped Superclasses)
實體可以繼承自一個超類,這個超類提供了持久化實體狀態(即屬性或欄位)和對映資訊,但它本身不是一個實體。通常情況下,這種超類對映的的目的是定義多個實體共有的狀態和對映資訊。
對映超類和實體不一樣,它不能夠被查詢,所以不能作為引數傳遞給EntityManager或Query 介面進行操作。對映超類定義的持久化關係必須是單向的。
抽象類或具體的類都可以作為對映超類,使用@MappedSuperclass註解(或mapped-superclass XML描述符元素)來指定對映超類。
對映超類不會生成單獨的表,它的對映資訊作用於繼承自它的實體類。

對映超類能夠像實體類一樣被對映,只是它的對映將作用於繼承自它的實體類,因為它本身不存在單獨的表。當作用於子類時,繼承的對映資訊將作用於子類對應的表上。子類可以通過@AttributeOverride和AssociationOverride註解或對應的XML描述符元素來覆蓋對映超類的對映資訊。下面來看一個例項:

package com.mikan;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public class Employee implements Serializable {

	private static final long serialVersionUID = -7674269980281525370L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	protected Integer empId;
	
	@Column
	protected String name;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "FT_EMP")
public class FullTimeEmployee extends Employee {

	private static final long serialVersionUID = 9115429216382631425L;

	// 繼承對映超類的empId和name屬性

	@Column
	private Double salary;

	// getter/setter方法
	
}

package com.mikan;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "PT_EMP")
public class PartTimeEmployee extends Employee {

	private static final long serialVersionUID = -6122347374515830424L;

	// 繼承對映超類的empId和name屬性

	@Column(name = "hourly_wage")
	private Float hourlyWage;

	// getter/setter方法
	
}
其中Employee是對映超類,它包括兩個欄位(即上面所說的狀態)和相應的對映資訊,只是這些對映資訊都由子類實體FullTimeEmployee和PartTimeEmployee繼承,它本身不會生成對應的表。只會生成FT_EMP和PT_EMP兩個表,這兩個表中的欄位包括了子類本身的欄位和從對映超類中繼承的欄位。即FT_EMP表的欄位為:empId、name、salary,而PT_EMP表的欄位為:empId、name、hourly_wage。

三、實體類繼承非實體類
一個實體類可以繼承一個非實體超類,這個非實體超類可以是具體的類,也可以是抽象類。
這個非實體超類僅被作為一種繼承行為,它的狀態不會被持久化。所有繼承自非實體超類的狀態(即屬性或欄位)在實體子類中都不會被持久化,實體管理器不會管理這些狀態。非實體超類上的所有註解都會被忽略。非實體超類不能作為引數傳遞給EntityManager或Query 介面進行操作。
下面來看一個例項:
public class Cart {
	protected Integer operationCount; // transient state

	public Cart() {
		operationCount = 0;
	}

	public Integer getOperationCount() {
		return operationCount;
	}

	public void incrementOperationCount() {
		operationCount++;
	}
}

@Entity
public class ShoppingCart extends Cart {
	Collection<Item> items = new Vector<Item>();

	public ShoppingCart() {
		super();
	}

	@OneToMany
	public Collection<Item> getItems() {
		return items;
	}

	public void addItem(Item item) {
		items.add(item);
		incrementOperationCount();
	}
}

ShoppingCart對應的表中不會包含operationCount欄位。