Hibernate擅长表示强类型、静态对象模型。并非所有应用程序都如此。 基于元数据
的应用程序在数据库中定义实体类型信息。对象模型和关系模型都支持动态添加新类型,甚至可能重新定义现有类型。实际上,大多数复杂应用程序都包含静态模型和动态模型的混合。
假设我们的系统支持各种类型的条目,每个条目都有专门的属性。如果有一个静态、预定义的条目类型集,我们可能会使用继承来建模这个。但是,如果新类型可以由用户动态定义 - 类型定义存储在数据库中呢?
我们可以定义一个ItemType类来表示条目类型的定义。每个ItemType实例将拥有一个ItemTypeAttribute实例的集合,每个实例代表适用于该特定条目类型的命名属性。ItemType和ItemTypeAttribute定义了 /元模型/。
每个Item实例将有一个唯一的ItemType,并将拥有一个ItemAttributeValue实例的集合,代表适用ItemTypeAttributes的实体值。
http://hibernate.sourceforge.net/metadata.gif
元模型类的映射非常简单。真正有趣的是,ItemType和ItemTypeAttribute是应该启用二级缓存的类的完美例子:更新很少,实例相对较少,实例在许多用户和许多Item类实例之间共享。
ItemType和ItemTypeAttribute的映射可能看起来像这样
<class name="ItemType">
<cache usage="nonstrict-read-write"/>
<id name="id">
<generator class="native"/>
</id>
<property name="name"
not-null="true"
length="20"/>
<property name="description"
not-null="true"
length="100"/>
<set name="attributes"
lazy="true"
inverse="true">
<key column="itemType"/>
<one-to-many class="ItemTypeAttribute"/>
</set>
</class>
<class name="ItemTypeAttribute">
<cache usage="nonstrict-read-write"/>
<id name="id">
<generator class="native"/>
</id>
<property name="name"
not-null="true"
length="20"/>
<property name="description"
not-null="true"
length="100"/>
<property name="type"
type="AttributeTypeUserType"
not-null="true"/>
<many-to-one name="itemType"
class="ItemType"
not-null="true"/>
</class>
我们不启用这些类的代理,因为我们预计实例始终会被缓存。我们将自定义类型AttributeTypeUserType的定义留给您!
Item和ItemAttributeValue的映射也非常简单
<class name="Item"
lazy="true">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="type"
class="ItemType"
not-null="true"
outer-join="false"/>
<set name="attributeValues"
lazy="true"
inverse="true">
<key column="item"/>
<one-to-many class="Item"/>
</set>
</class>
<class name="ItemAttributeValue"
lazy="true">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="item"
class="Item"
not-null="true"/>
<many-to-one name="type"
class="ItemTypeAttribute"
not-null="true"
outer-join="false"/>
<property name="value" type="AttributeValueUserType">
<column name="intValue"/>
<column name="floatValue"/>
<column name="datetimeValue"/>
<column name="stringValue"/>
</property>
</class>
请注意,我们必须显式设置outer-join="false"以防止Hibernate从缓存中检索相关对象,这些对象我们预期会在缓存中找到。
最后,我们需要定义自定义类型AttributeValueUserType,它将ItemAttributeValue的值存储在其正确类型的数据库列中。
public class AttributeValueUserType implements UserType {
public int[] sqlTypes() {
return new int[] { Types.BIGINT, Types.DOUBLE, Types.TIMESTAMP, Types.VARCHAR };
}
public Class returnedClass() { return Object.class; }
public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException {
Long intValue = (Long) Hibernate.LONG.nullSafeGet(rs, names[0], owner);
if (intValue!=null) return intValue;
Double floatValue = (Double) Hibernate.DOUBLE.nullSafeGet(rs, names[1], owner);
if (floatValue!=null) return floatValue;
Date datetimeValue = (Date) Hibernate.TIMESTAMP.nullSafeGet(rs, names[2], owner);
if (datetimeValue!=null) return datetimeValue;
String stringValue = (String) Hibernate.STRING.nullSafeGet(rs, names[3], owner);
return stringValue;
}
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
Hibernate.LONG.nullSafeSet( st, (value instanceof Long) ? value : null, index );
Hibernate.DOUBLE.nullSafeSet( st, (value instanceof Double) ? value : null, index+1 );
Hibernate.TIMESTAMP.nullSafeSet( st, (value instanceof Date) ? value : null, index+2 );
Hibernate.STRING.nullSafeSet( st, (value instanceof String) ? value : null, index+3 );
}
public boolean equals(Object x, Object y) throws HibernateException {
return x==null ? y==null : x.equals(y);
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false;
}
}
就这样!
更新:我不知道我当时在想什么!当然,我们需要能够查询我们项目的属性,所以 AttributeValueUserType 应该是一个 /复合/ 自定义类型!
public interface CompositeUserType {
public String[] getPropertyNames() {
return new String[] { "intValue", "floatValue", "stringValue", "datetimeValue" };
}
public Type[] getPropertyTypes() {
return new Type[] { Hibernate.LONG, Hibernate.DOUBLE, Hibernate.STRING, Hibernate.TIMESTAMP };
}
public Object getPropertyValue(Object component, int property) throws HibernateException {
switch (property) {
case 0: return (component instanceof Long) ? component : null;
case 1: return (component instanceof Double) ? component : null;
case 2: return (component instanceof String) ? component : null;
case 3: return (component instanceof Date) ? component : null;
}
throw new IllegalArgumentException();
}
public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
throw new UnsupportedOperationException();
}
public Class returnedClass() {
return Object.class;
}
public boolean equals(Object x, Object y) throws HibernateException {
return x==null ? y==null : x.equals(y);
}
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
//as above!
}
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
//as above!
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false;
}
public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
return value;
}
public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException {
return value;
}
}
现在我们可以编写如下查询
from Item i join i.attributeValues value where value.name = 'foo' and value.inValue = 69