基于元数据的应用程序

发布者    |      

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

返回顶部