混合继承策略
可以把一个类层次结构映射到单张表,但是对于特定的子类,则通过外键映射策略切换到单独的表,就像使用每个子类一张表一样。使用<join>映射元素,这种转换是可能的:<hibernate-mapping package="cn.jbit.hibernate.entity5"> <class name="BillingDetails8" table="BILLING_DETAILS8"> <id name="id" column="BILLING_DETAILS_ID" type="integer"> <generator class="native"/> </id> <discriminator column="BILLING_DETAILS_TYPE" type="string" /> <property name="owner"> <column name="OWNER" not-null="true"/> </property> <subclass name="CreditCard8" discriminator-value="CC"> <join table="CREDIT_CARD8"> <key column="CREDIT_CARD_ID" /> <property name="number" column="CC_NUMBER" type="string" length="20" not-null="false"/> <property name="expMonth" column="EXP_MONTH" type="string" length="20" not-null="false"/> <property name="expYear" column="EXP_YEAR" type="string" length="20" not-null="false"/> </join> </subclass> <subclass name="BankAccount8" discriminator-value="BA"> <property name="account" column="BA_ACCOUNT" type="string" length="20" not-null="false"/> <property name="bankname" column="BANK_NAME" type="string" length="20" not-null="false"/> <property name="swift" column="SWIFT" type="string" length="20" not-null="false"/> </subclass> </class> </hibernate-mapping><join>元素集合了一些属性,并告诉Hibernate到一个二级表中获取它们。
Java Persistence也通过注解支持这种混合的继承映射策略。就像前面所做的那样,用InheritanceType.SINGLE_TABLE映射超类BillingDetails。现在映射你想要从单张表分到一张二级表的子类。
@Entity @DiscriminatorValue("CC") @SecondaryTable( name="CREDIT_CARD9", pkJoinColumns=@PrimaryKeyJoinColumn(name="CREDIT_CARD_ID") ) public class CreditCard9 extends BillingDetails9 { //映射二级表属性 @Column(table="CREDIT_CARD9",name = "CC_NUMBER", nullable = false) private String number; @Column(table="CREDIT_CARD9",name = "EXP_MONTH", nullable = false) private String expMonth; @Column(table="CREDIT_CARD9",name = "EXP_YEAR", nullable = false) private String expYear; }注意:你要用二级表的名称映射被移到二级表里面的所有属性。
选择策略
可以把所有的映射策略应用到抽象类和接口。接口可能没有状态,但是可能包含访问方法声明,因此可以像抽象类一样地处理它们。可以用<class>、<union-subclass>、<subclass>或者<joined-subclass>映射接口,并可以用<property>映射任何被声明或者被继承的属性。Hibernate不会试图实例化抽象类,即使你查询或者加载了它。以下是一些经验法则:
1)如果你不需要多态关联或者查询,就倾向于每个具体类一张表。
2)如果你一定要多态关联(对超类的关联,及由此在运行时通过具体类的动态解析对层次结构中的所有类的关联)或者查询,并且子类相对地声明几种属性(尤其当子类之间的主要区别在于它们的行为中)时,则倾向于每个类层次结构一张表。你的目标是把可为空的列数减到最少,并让你自己(和数据库管理员)确信的规范化的Schema在长期运行中不会产生问题。
3)如果你一定需要多态的关联或者查询,并且子类声明多个属性(子类的不同主要在于它们所持有的数据),则倾向每个子类一张表。或者,根据继承层次结构的宽度和深度,以及相对于联合的可能联结成本,使用每个具体类一张表。
默认情况下,仅对简单的问题选择每个类层次结构一张表。对于更复杂的案例,就应该考虑每个子类一张表的策略。
最后,也可以在单个的映射文件中使用<union-subclass>、<subclass>和<joined-subclass>映射元素(作用一级元素,而不是<class>)。然后必须声明被扩展的类,例如
<subclass name="CreditCard" extends="BillingDetails">,且必须在子类映射文件之前程序化地加载基类映射(在XML配置文件中列出映射资源小清单时,不必担心这个顺序)。
一些注意点:
如果依赖Java Persistence注解,包含潜在大值的映射属性则稍有不同。默认情况下,类型java.lang.String的一个属性被映射到SQL VARCHAR列(或者相当的列,取决于SQL方言)。如果想要把java.lang.String、char[]、Character[],或者甚至java.sql.Clob类型属性映射到CLOB列,就要用@Lob注解映射它:@Lob @Column(name = "ITEM_DESCRIPTION") private String description; @Lob @Column(name = "ITEM_IMAGE") private byte[] image;最后,注意Hibernate和JPA都对Serializable(可序列化)的任何属性类型提供序列回滚。这种映射类型把属性的值转换为随后保存在VARBINARY(或者相当的)列中的字节流。当属性的所有者被加载时,属性值是反序列化的。自然地,使用这种策略要非常谨慎(数据存在得比应用程序更长久),并且它可能只对临时数据(用户偏爱、登录会话数据等)有用。
Hibernate也理解type="java.lang.String",因此它不必使用反射。这种方法不太适用的最重要案例是java.util.Date属性。默认情况下,Hibernate认为java.util.Date是一个timestamp映射。如果不希望日期和时间信息都持久化,就要显式地指定type="time"或者type="date"。
利用JPA注解,自动侦测属性的映射类型,就像在Hibernate中一样。对于java.util.Date或者java.util.Calendar属性,Java Persistence标准要求用@Temporal注解选择精确度:
@Temporal(TemporalType.TIMESTAMP) @Column(nullable = false,updatable = false) private Date startDate;与之相当的JPA XML描述符如下:
<entity class="auction.model.Item" access="FIELD"> <attributes> ... <basic name="startDate"> <column nullable="false" updatable="false" /> <temporal>TIMESTAMP</temporal> </basic> </attributes> </entity>对于每个内建的映射类型,都由类org.hibernate.Hibernate定义常量。例如,Hibernate.STRING表示string映射类型。这些常量对于查询参数绑定很有用:
session.createQuery("from Item i where i.description like :desc") .setParameter("desc",d,StringType.INSTANCE) .list();注意,也可以在这个例子中用setString()实参绑定方法。