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