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

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