Hibernate实战_笔记25(细粒度的模型和映射)

细粒度的模型和映射

      JDK带来的值类型的类(例如String或者基本类型),以及由应用程序的开发员定义的值类型的类(例如Address和MontaryAmount)。
      首先,映射使用JDK类型的持久化类属性,并学习基础的映射元素和属性。然后着手定制的值类型类,并把它们映射为可嵌入的组件。

映射基础属性

      如果映射持久化类,无论它是实体还是值类型,所有持久化属性都必须在XML映射文件中被显式地映射。另一方面,如果用注解映射类,它的所有属性都默认为是持久化的。可以用@javax.persistence.Transient注解给属性进行标识来所它们排除,或者使用transient的Java关键字(通常只给Java序列化排除字段)。
在一个JPA XML描述符中,可以排除一个特定的字段或者属性:
<entity class="auction.model.User" access="FIELD">
    <attributes>
        ...
        <transient name="age"/>
    </attributes>
</entity>
      一个典型的Hibernate属性映射定义了一个POJO的属性名称、一个数据库列名和一个Hibernate类型的名称,并且它可能经常省略类型。因此,如果description是(Java)类型java.lang.String的一个属性,Hibernate就默认使用Hibernate类型string。
      Hibernate用反射决定属性的Java类型。因而,下列映射是等价的:
<property name="description" column="DESCRIPTION type="string"/>
<property name="description" column="DESCRIPTION"/>
如果列名与属性名相同(忽略大小写),那么列名甚至可以省略。
JPA是基于异常模型的一个配置,因此可以依赖默认。如果持久化类的一个属性没有被注解,就应用下列规则:
1)如果属性是JDK类型,它自动就是持久化的。换句话说,它在Hibernate XML映射文件中像<property name="propertyName"/>一样处理。
2)如果属性的类被注解为@Embeddable,它就被映射为自己的类的组件。
3)如果属性的类型是Serializable,它的值以序列化的形式保存。
      如果不想依赖这些默认,就在一个特定的属性上应用@Basic注解。@Column注解相当于XML的<column>元素。以下是如何按要求声明一个属性值的例子:
@Basic(optional = false)
@Column(nullable = false)
public BigDecimal getInitialPrice { return initialPrice; }
      @Basic注解把属性标识为在Java对象级上不可选。第二个设置,列映射上的nullable = false,只负责NOT NULL(非空)数据库约束的生成。
      在JPA XML描述符中,这个映射看起来是相同的:
<entity class="auction.model.Item" access="PROPERTY">
    <attributes>
        <basic name="initialPrice" optional="false">
            <column nullable="false"/>
        </basic>
    </attributes>
</entity>
用于属性的注解永远都在访问方法中吗?

1、定制属性访问

      类的属性由持久化引擎直接(通过字段)或者间接(通过获取方法和设置方法属性访问方法)访问。在XML映射文件中,用hibernate-mapping的default-access="field|property|noop"属性给类控制默认的访问策略。被注解的实体从强制的@Id注解的位置继承默认的访问策略。如果@Id已经在字段中(而不是获取方法中)声明,      那么所有其他的属性映射注解(如货品description属性的列名)也在字段中声明:
@Column(name = "ITEM_DESCR")
private String description;

public String getDescription() { return description; }
      这是JPA规范定义的默认行为。然而,Hibernate允许利用@org.hibernate.annotations.AccessType(<strategy>)注解灵活地定制访问策略:
1)如果在类/实体级中设置AccessType,类的所有属性都根据选中的策略访问。属性级注解是在字段还是获取方法上,这取决于策略。
2)如果给属性访问默认或者显式地设置一个实体,字段中的AccessType("property")注解就把这个特定的属性转换为通过属性获取方法/设置方法的运行时访问。AccessType注解的位置仍然为字段。
如果给方法访问默认或者显式地设置一个实体,获取方法中的AccessType("field")注解就把这个特定的属性转换为通过同名字段的运行时访问。AccessType注解的位置仍然为获取方法。
3)任何@Embedded类都继承默认的或者显式声明的自己根实体类的访问策略。
4)任何@MappedSuperclass属性都通过被映射实体类的默认或者显式声明的访问策略而被访问。
也可以用access属性在Hibernate XML映射中控制属性级上的访问策略:
<property name="description"
    column="DESCR"
    access="field"/>
      或者,可以用default-access属性给一个根<hibernate-mapping>元素内部的所有类映射设置访问策略。
除了字段和属性访问之外,另一个可能有用的策略是noop。它映射Java持久化类中不存在的属性。(Java类中没有属性,实体映射文件含有property access="noop",数据库有对应的属性)。
      如果没有合适的内建访问策略,也可以通过实现接口org.hibernate.property.PropertyAccessor定义自己的定制属性访问策略。
      有些属性根本不映射到列。特别是衍生属性(derived property),它从SQL表达式中取值。

2、使用衍生属性

      衍生属性的值在运行时计算,通过对利用formula属性定义的表达式求值。例如,可以把totalIncludingTax属性映射到SQL表达式:
<property name="totalIncludingTax"
    formula="TOTAL + TAX_RATE * TOTAL"
    type="big_decimal"/>
      这个给定的SQL公式在每次从数据库获取实体时求值(并且在任何其他时间不求值,因此如果其他的属性被修改,这个结果就可能过时)。属性没有列属性(或者子元素),并且永远不会出现SQL的INSERT或者UPDATE中,而只在SELECT中。公式可能指向数据库表的列,它们可以调用SQL函数,甚至包括SQL子查询。
公式也可使用Hibernate注解:
@org.hibernate.annotations.Formula("TOTAL + TAX_RATE * TOTAL")
public BigDecimal getTotalIncludingTax(){
    return totalIncludingTax;
}
下列示例使用一个关联的子查询来计算一件货品所有出价的平均值:
<property
    name="averageBidAmount"
    type="big_decimal"
    formula="(select AVG(b.AMOUNT) from
        BID b where b.ITEM_ID  = ITEM_ID"/>
另一种特殊的属性依赖数据库生成的值。

3、生成的和默认的属性值

      想象类的一个特定属性有着数据库生成的值,通常在第一次插入实体行的时候。典型的数据库生成的值是创建的时间戳、对一件货品的默认价格,以及每次修改时运行的触发器。
      典型的Hibernate应用程序需要刷新包含由数据库为其生成值的任何属性的对象。然而,标识属性为已生成(generated),让应用程序把这个责任委托给了Hibernate。本质上,每当Hibernate给定义了已生成属性的实体执行SQL INSERT或者UPDATE时,它在获取已生成的值之后立即执行SELECT。使用property映射中的generated开关启用这个自动刷新:
<property name="lastModified"
    column="LAST_MODIFIED"
    update="false"
    insert="false"
    generated="always"/>
      标记为数据库生成的属性还必须是非可插入和非可更新的,用insert和update属性进行控制它们。如果两者都设置为false,属性的列就永远不会出现在INSERT或者UPDATE语句中——属性值是只读的。而且,通常不在类中给不可变的属性添加公有的设置方法(并切换到字段访问)。
利用注解,通过@Generated的Hibernate注解声明不可变性(和自动刷新):
@Column(updatable = false, insertable = false)
@org.hibernate.annotations.Generated(
    org.hibernate.annotations.GenerationTime.ALWAYS
)
private Date lastModified;
      数据库生成的属性值的一个特殊情况是默认值。例如,你可能想要实现一种规则,即每件拍卖货品的成本至少为$1。首先,得把它作为INITIAL_PRICE列的默认值添加到数据库目录:
create table ITEM(
    ...
    INITIAL_PRICE number(10,2) default ‘1‘,
    ...
);
      如果使用Hibernate的Schema导出工具hbm2ddl,则可以通过把default属性添加到属性映射来启用这项输出:
<class name="Item" table="TBL_ITEM"
    dynamic-insert="true" dynamic-update="true">
    ...
    <property name="initialPrice" type="big_decimal">
        <column name="INITIAL_PRICE" default="‘1‘" generated="insert"/>
    </property>
</class>
      注意,你还必须启用动态的插入并更新语句的生成,以便包含默认值的列不会被包括在每个语句中,如果它的值为null(否则将插入NULL,而不是默认值)。此外,已经被变成持久化但尚未清除到数据库,将不在对象属性上设置默认值。换句话说,你需要执行一个显式的清除:
Item newItem = new Item(...);
session.save(newItem);

newItem.getInitialPrice(); //is null

session.flush();
//Hibernate does a SELECT automatically

newItem.getInitialPrice(); //is $1
因为设置了generated="insert",Hibernate知道读取数据库生成的属性值立即需要一个额外的SELECT。
可以用注解把默认的列值映射为列的DDL定义的一部分:
@Column(
    name="INITIAL_PRICE",
    columnDefinition="number(10,2) default ‘1‘")
@org.hibernate.annotations.Generated(
    org.hibernate.annotations.GenerationTime.INSERT
)
private BigDecimal initialPrice;
      columnDefinition属性包括列DDL的完整属性,包含数据类型和所有约束。记住,一个实际上不可移植的SQL数据类型可能把注解映射绑定到一个特定的数据库管理系统。

Hibernate实战_笔记25(细粒度的模型和映射),布布扣,bubuko.com

Hibernate实战_笔记25(细粒度的模型和映射)

上一篇:Linux下的无名管道pipe的设计


下一篇:Hibernate实战_笔记26(映射组件)