Hibernate筆記——10.繼承映射

Hibernate中的繼承指的是實體類之間的繼承。能夠繼承屬性。本例中書中使用了Person、Customer、Manager和Employee這四個實體類來距離。其中Person衍生出了Customer和Employee,而Employee又衍生出了Employee,所以說Employee的屬性是最多的。除此之外,Person還有一個組件屬性Address。Customer與Employee是N-1映射,而Employee與Manager也是1-N映射。

我們先來寫一下大家都有的Address。

public class Address
{
	private String detail;
	private String zip;
	private String country;

	public Address(){}
	public Address(String detail , String zip , String country)
	{
		this.detail = detail;
		this.zip = zip;
		this.country = country;
	}
}

由於我們在Person類中配置組件屬性的列映射關係,因此這裏的Address就是一個普通的bean。Hibernate支持三種繼承映射關係:

  • 整個類層次對應一個表

  • 連接子類的映射策略

  • 每個具體類對應一個表


整個類層次對應一個表

這是默認的策略,所有子類跟父類在一起,表中有很多類,包含了所有的屬性,因此有的列是空的。我們使用@DiscriminatorColumn來配置一個識別列,可以指定列的名字,列中數據類型。有了這個列,我們還要在所有的子類跟父類中使用@Discriminator來指定一個表示,以表示整個表中的某一行具體是屬於哪個實體。

@Entity
// 定義辨別者列的列名爲person_type,列類型爲字符串
@DiscriminatorColumn(name="person_type" ,
	discriminatorType=DiscriminatorType.STRING)
// 指定Person實體對應的記錄在辨別者列的值爲"普通人"
@DiscriminatorValue("allkind")
@Table(name="person_inf")
public class Person
{
	@Id @Column(name="person_id")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer id;
	private String name;
	private char gender;
	
	@Embedded
	@AttributeOverrides({
		@AttributeOverride(name="detail",
		column=@Column(name="address_detail")),
		@AttributeOverride(name="zip",
		column=@Column(name="address_zip")),
		@AttributeOverride(name="country",
		column=@Column(name="address_country"))
	})
	private Address address;
	
	public Person(){}
	public Person(Integer id , String name , char gender)
	{
		this.id = id;
		this.name = name;
		this.gender = gender;
	}
}

這個實體中在類之前我們定義了標識列,以及這個類在標識列中的標識。此外這裏面定義了一個address組件。下面我們看員工類。

@Entity
// 指定Employee實體對應的記錄在辨別者列的值爲"員工"
@DiscriminatorValue("employee")
@Table(name="employee_inf")
public class Employee extends Person
{
	private String title;
	private double salary;
	
	// 定義和該員工保持關聯的Customer關聯實體
	@OneToMany(cascade=CascadeType.ALL
		, mappedBy="employee" , targetEntity=Customer.class)
	private Set<Customer> customers
		= new HashSet<>();
	
	// 定義和該員工保持關聯的Manager關聯實體
	@ManyToOne(cascade=CascadeType.ALL
		,targetEntity=Manager.class)
	@JoinColumn(name="manager_id", nullable=true)
	private Manager manager;

        public Employee(){}
	public Employee(String title , double salary)
	{
		this.title = title;
		this.salary = salary;
	}
}
@Entity
// 指定Manager實體對應的記錄在辨別者列的值爲"經理"
@DiscriminatorValue("manager")
@Table(name="manager_inf")
public class  Manager extends Employee
{
	private String department;
	// 定義和該經理保持關聯的Employee關聯實體
	@OneToMany(cascade=CascadeType.ALL
		, mappedBy="manager" , targetEntity=Employee.class)
	private Set<Employee> employees
		= new HashSet<>();
	// 無參數的構造器
	public Manager(){}
	public Manager(String department)
	{
		this.department = department;
	}

}
@Entity
// 指定Customer實體對應的記錄在辨別者列的值爲"顧客"
@DiscriminatorValue("customer")
@Table(name="customer_inf")
public class Customer extends Person
{
	private String comments;
	
	// 定義和該顧客保持關聯的Employee關聯實體
	@ManyToOne(cascade=CascadeType.ALL
		,targetEntity=Employee.class)
	@JoinColumn(name="employee_id", nullable=true)
	private Employee employee;
	// 無參數的構造器
	public Customer(){}
	public Customer(String comments)
	{
		this.comments = comments;
	}
}

下面來看測試代碼。這個代碼我直接衝書中給的光盤中的源代碼粘過來的。

private void createAndStorePerson()
	{
		Session session = HibernateUtil.currentSession();
		Transaction tx = session.beginTransaction();
		// 創建一個普通員工
		Employee zhu = new Employee();
		// 設置員工的基本屬性
		zhu.setName("老朱");
		zhu.setTitle("項目組長");
		zhu.setGender('男');
		zhu.setSalary(4500);
		// 設置員工的組件屬性
		zhu.setAddress(new Address("廣州","523034","中國"));
		// 創建第二個員工
		Employee zhang = new Employee();
		// 設置該員工的基本屬性
		zhang.setName("張美麗");
		zhang.setTitle("項目分析");
		zhang.setGender('女');
		zhang.setSalary(5500);
		// 設置該員工的組件屬性
		zhang.setAddress(new Address("廣州","523034","中國"));
		// 創建一個經理對象
		Manager grace = new Manager();
		// 設置經理對象的基本屬性
		grace.setName("Grace");
		grace.setTitle("項目經理");
		grace.setGender('女');
		grace.setSalary(12000);
		// 設置經理的組件屬性
		grace.setAddress(new Address("加州" , "523034" , "美國"));
		// 設置經理的管轄部門屬性
		grace.setDepartment("研發部");
		// 設置第二個員工和grace之間的關聯關係
		zhang.setManager(grace);
		// 創建一個Customer對象
		Customer he = new Customer();
		// 設置Customer對象的基本屬性
		he.setName("小賀");
		he.setGender('男');
		// 設置Customer對象的組件屬性
		he.setAddress(new Address("湖南" , "233034" , "中國"));
		he.setComments("喜歡購物");
		// 建立Customer對象和grace對象的關聯關係
		he.setEmployee(grace);
		// 創建一個普通Person對象
		Person lee = new Person();
		// 設置Person對象的基本屬性
		lee.setName("crazyit.org");
		lee.setGender('男');
		// 設置Person對象的組件屬性
		lee.setAddress(new Address("天河" , "434333" , "中國"));
		// 持久化所有實體。
		session.save(lee);
		session.save(grace);
		session.persist(zhu);
		session.persist(zhang);
		session.save(he);
		tx.commit();
		HibernateUtil.closeSession();
	}

mysql> select * from person_inf;

+-------------+-----------+-----------------+----------------+-------------+--------+--------------+----------+--------+-------+------------+-------------+------------+

| person_type | person_id | address_country | address_detail | address_zip | gender | name         | comments | salary | title | department | employee_id | manager_id |

+-------------+-----------+-----------------+----------------+-------------+--------+--------------+----------+--------+-------+------------+-------------+------------+

| ???         |         1 | ??              | ??             | 434333      | ?      | crazyit.org  | NULL     |   NULL | NULL  | NULL       |        NULL |       NULL |

| ??          |         2 | ??              | ??             | 523034      | female | Grace        | NULL     |  12000 | ????  | ???        |        NULL |       NULL |

| ??          |         3 | USA             | DC             | 22202       | male   | Zhijin Zhang | NULL     |   9500 | vp    | NULL       |        NULL |       NULL |

| ??          |         4 | NULL            | NULL           | NULL        | NULL   | NULL         | NULL     |      0 | NULL  | NULL       |        NULL |          2 |

| ??          |         5 | ??              | ??             | 233034      | ?      | ??           | ????     |   NULL | NULL  | NULL       |           2 |       NULL |

| general     |         6 | ??              | ??             | 434333      | ?      | crazyit.org  | NULL     |   NULL | NULL  | NULL       |        NULL |       NULL |

| manager     |         7 | ??              | ??             | 523034      | female | Grace        | NULL     |  12000 | ????  | ???        |        NULL |       NULL |

| employee    |         8 | USA             | DC             | 22202       | male   | Zhijin Zhang | NULL     |   9500 | vp    | NULL       |        NULL |       NULL |

| employee    |         9 | NULL            | NULL           | NULL        | NULL   | NULL         | NULL     |      0 | NULL  | NULL       |        NULL |          7 |

| customer    |        10 | ??              | ??             | 233034      | ?      | ??           | ????     |   NULL | NULL  | NULL       |           7 |       NULL |

+-------------+-----------+-----------------+----------------+-------------+--------+--------------+----------+--------+-------+------------+-------------+------------+

由於書中代碼使用了中文,所以產生了亂碼。person_type列指明瞭後面的數據分別屬於哪個實體類。由於不是所有的實體類都擁有全部的屬性,因此有很多空值。所以所有的子類都不能有非空約束。不過他也有它的好處。由於都在同一表中,因此查性能好。


連接子類的映射策略

使用這種策略需要在根類中使用@inheritance指明映射策略。它的strategy屬性支持InheritanceType的種類有三個,SINGLE_TABLE標識整個類層次對應一個表得映射策略,是默認值。JOINED是連接子類的映射策略。TABLE_PER_CLASS是每個具體類對一個一個表得映射策略。這種策略下我們使用JOINED。父類實體保存在父類自己的表中,而子類由父類表跟子類表共同儲存。也就是說共有的屬性在父類,獨有的纔在子類表。這種策略中不需要使用標識列了,取而代之的是@Inheritance(strategy=InheritanceType.JOINED)。所有的子表都是用person_id這個主鍵作爲外鍵。查詢的時候,從所有的表中找出主鍵相同的信息,然後拼在一起。子類也可以增加非空約束。


每個具體類對應一個表

數據不分割,每個子類都有一個表。父類表中沒有信息,每個子類表中都有它的全部屬性。這種方式很難看出繼承關係,只是多個實體之間的主鍵有某種連續性。因此主鍵不能夠用 IDENTITY或者AUTO。跟上面的一樣,在根類中使用@Inheritance修飾。由於不能使用自增長主鍵,因此我們這裏使用GenericGenerator註解指定使用hilo主鍵生成策略。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章