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