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
 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
 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主键生成策略。