特殊属性映射:ID、Version和Transient

实体类有一些特殊的属性,典型的有两个:实体标识符属性和乐观锁属性。由于这两个属性是所有的实体通用的,我将它们定义在一个抽象基类BaseEntity中,由所有的实体类直接或间接继承。BaseEntity被类级逻辑注解@MappedSuperclass标识,其意义在下文《继承映射》中讲述。

一、ID属性

前文说过,实体必须拥有唯一的标识符,用于区别同类的其他实体。实体标识符属性用@Id逻辑注解标识。这个注解可以继承。

一个类要成为实体类,至少要同时符合下面的条件:

  • 在类级拥有@Entity注解。
  • 在自有或继承的可以作为标识符的属性上拥有@Id注解。

下面是@Id注解标识的标识符属性:

@MappedSuperclass
public abstract class BaseEntity implements Serializable {

    @Id
    @GeneratedValue
    private int id;
}

实体的ID属性是关联实体对象实例和数据行的关键。通常实体利用这个属性值作为对象标识符而数据库表利用它作为数据行的主键值。

实体ID既可以由应用代码设置(例如UUID就是很好的候选),也可以由数据库生成。上面的例子中,整型的标识符字段id除了被注解为@Id之外还被注解为GeneratedValue。后者告知JPA实现框架由数据库负责生成实体ID值。当需要由数据库生成ID时,ID属性的类型通常受限为整数或长整数。

值对象没有ID,所以不需要拥有用@Id注解的属性。

二、版本属性

JPA通过在实体类上定义一个类型为整数/长整数并注解为@Version的属性来添加对表数据的乐观锁定的支持。

这个属性会随着对实体的每一次修改而递增它的值。当JPA获取一个对象时,会记录下它当时的版本值。当修改后再次保存该实体时,JPA会再次访问数据库,检查这个版本值是否仍然是当初的值。如果是,说明这个实体在这段时间内没有被别人修改过,可以顺利保存。如果不是,说明这个在这段时间内实体已经被别人修改过,保存会失败,并抛出乐观锁异常。通过这样的方式,可以防止数据被并发修改。

版本属性不是必须的,但强烈建议在所有的实体类上添加这个属性。

由于我决定让所有的实体都拥有版本属性,所以我把这个属性定义在所有实体的基类BaseEntity中:


@MappedSuperclass
public abstract class BaseEntity implements Serializable {

    @Version
    private int version;

}

三、瞬态属性

在实体和值对象中,默认状态下所有的实例属性都是要持久化到数据库中的,与数据库中的列有对应关系。但是以下的属性不会持久化到数据库中:

  • 静态属性
  • 添加有transient修饰符的实例属性
  • 添加有@Transient注解的实例属性

因此,如果你想在对象持久化范围中排除某些属性,可以给该属性添加transient修饰符或@Transient注解。

@Entity
@Table(name = "order_lines")
public class OrderLine extends BaseEntity {

    @Transient
    private Money subTotal;
}

results matching ""

    No results matching ""