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主键生成策略。
转载于:https://blog.51cto.com/mengcao/1692960