之前所讲述的实体都是标注在具体类(concrete)上,事实上,实体也可以标注在抽象类abstract上。
例如,将7.3.3小节中使用的EmoloyeeEO实体改成抽象类,代码如下所示:
@Entity
@Table(name = "tb_employee")
@Inheritance(strategy=InheritanceType. SINGLE_TABLE)
@DiscriminatorColumn(name="employee_type",discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("employee")
public abstract class EmployeeEO implements Serializable {
private Integer id;
private String name;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
/*getter和setter方法省略*/
/*一些abstract抽象方法*/
}
使用标注在抽象类的实体与具体实现的实体的区别是:由于抽象类不能实例化,所以使用以下代码是行不通的:
EmployeeEO employee = new EmployeeEO();
employee.setName("Janet");
entityManager.persist(employee);
这也是因为Java中抽象类本身的意义决定的,抽象类不能实例化。
由于不能创建抽象类的实例,所以在JPA映射中也可以理解为不能创建抽象类的实体(Entity)。但是,抽象实体也是实体,它可以使用EQL语句查询,例如:
SELECT
employee
FROM
EmployeeEO employee
使用抽象类的好处是,当需要所有的类都要增加一个方法时,这时只需要在抽象类中增加一个抽象的方法。这样所有的类再去实现这个方法,重构时就很变得很容易了。例如为所有类添加一个getSnapshot()的方法,代码如下所示:
@Entity
@Table(name = "tb_employee")
@Inheritance(strategy=InheritanceType. SINGLE_TABLE)
@DiscriminatorColumn(name="employee_type",discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("employee")
public abstract class EmployeeEO implements Serializable {
/*抽象方法,不做持久化映射*/
@Transient
abstract String getSnapshot();
}
此时,它的子类RegularEmployeeEO的代码如下所示:
@Entity
@DiscriminatorValue("regular")
public class RegularEmployeeEO extends EmployeeEO{
/*代码省略*/
@Transient
String getSnapshot() {
return "这是RegularEmployeeEO";
}
}
这样,可以看到,一旦在父类抽象类EmployeeEO中增加了抽象方法,其子类就必须实现这个方法,否则编译器将不能通过编译,这样就保证了当进行重构时,不会发生很难排查的错误。