Hibernate中any元素的应用体会

转自:http://www.iteye.com/topic/8900

Hibernate中any元素的应用体会

关联(Associations)是Hibernate核心概念之一,比较常用的有:

many-to-one,one-to-one,one-to-many,many-to-many

Hibernate还提供了另外一种关联——异类关联(HeterogeneousAssociations)

在Hibernate Reference (cn) 2.1.6中是这样说明的:
引用
6.10. 异类关联(Heterogeneous Associations) <many-to-any>和<index-many-to-any>元素提供真正的异类关联。这些元素和<any>元素工作方式是同样的,他们都应该很少用到。

下面针对<any>元素,谈一些自己的体会。

一、什么时候需要<any>元素

持久类中“一个属性”关联“另外一个指定的持久类”(几乎每个应用都有这种情况),多半会使用many-to-one,one-to-one这样的关联。映射到关系数据库中,也多半使用外键约束。

可能会遇到有这么一种特殊的情况,需要:持久类中“一个属性”关联“另外一些持久类”。

举个例子:Log类中使用logEntity属性关联一组业务持久类。(也就是说,在Log中记录不同业务类的实例对象)

如果使用many-to-one,则有很大的限制。首先,需要这些业务类都要继承一个超类,而且在数据库中必须有这个超类对应的表。在Hibernate提供的三种继承映射策略中,只能使用前两种:

1、“每棵类继承树使用一个表(tableperclasshierarchy)”

2、“每个子类一个表(tablepersubclass)”

第1种通常不大合适:所有的业务类映射为一张表,冗余过多,限制也多,增加一个业务类就需要修改表结构,不易扩展。

第2种的情况是:表的数量=业务表数量+一个超类表,子类表通过主键和超类表关联(所以实际上关系模型是一对一关联)。业务表数量比较多的时候,这种结构的性能和灵活性都有问题。

这时<any>元素就派上用场啦。

二、<any>元素的应用

1、类:

业务类:
public class BizOne {   

    private Long    id;   


    private String  bizOneDescription;   


    //Getters and Setters 省略   

}   

public class BizTwo {   


    private Long    id;   


    private String  bizTwoDescription;   


    private Date    createDate;   


    //Getters and Setters 省略   

}  
public class BizOne {
	private Long	id;
	private String	bizOneDescription;
	//Getters and Setters 省略
}
public class BizTwo {
	private Long	id;
	private String	bizTwoDescription;
	private Date    createDate;
	//Getters and Setters 省略
}

日志类:

public class MyLog {   

    private Long    id;   


    private Date    logDate;   


    private Object  logEntity;  //这就是<any>元素对应的属性。   


    //Getters and Setter 省略   

}  
public class MyLog {
	private Long	id;
	private Date	logDate;
	private Object	logEntity;	//这就是<any>元素对应的属性。
	//Getters and Setter 省略
}

2、hbm.xml和表结构:

这里只给出MyLog的hbm.xml(BizOne,BizTwo很简单,不提了):

<hibernate-mapping>   

     <class name="com.test.entity.MyLog" table="MyLog">   


        <id name="id" column="id">   


            <generator class="native"/>   

        </id>   

        <property name="logDate"/>   


        <any name="logEntity" meta-type="string" id-type="long">   


            <meta-value value="One" class="com.test.entity.BizOne"/>   


            <meta-value value="Two" class="com.test.entity.BizTwo"/>   


            <column name="entityMetaValue" length="20" />   


            <column name="entityId"/>   

        </any>   

    </class>   

</hibernate-mapping>  
<hibernate-mapping>
     <class name="com.test.entity.MyLog" table="MyLog">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="logDate"/>
        <any name="logEntity" meta-type="string" id-type="long">
          	<meta-value value="One" class="com.test.entity.BizOne"/>
          	<meta-value value="Two" class="com.test.entity.BizTwo"/>
            <column name="entityMetaValue" length="20" />
            <column name="entityId"/>
        </any>
    </class>
</hibernate-mapping>

表结构(MySQL):

create table MyLog (   
   id BIGINT NOT NULL AUTO_INCREMENT,   
   logDate datetime,   

   entityMetaValue VARCHAR(20);,   

   entityId BIGINT,   
   primary key (id);   
);  
create table MyLog (
   id BIGINT NOT NULL AUTO_INCREMENT,
   logDate datetime,
   entityMetaValue VARCHAR(20);,
   entityId BIGINT,
   primary key (id);
);

对<any>元素中子元素和属性的理解,可以结合生成的表结构,及其表中的数据(见3):

name:是持久类中属性名称。

meta-type:是下面meta-value元素中value的类型,如"string","character"等。

id-type:是引用类的主键类型。

meta-value元素中value:该值将保存到数据库表中,用来标识其后的class,即引用的持久类。请参考下面的数据。

meta-value元素中class:引用持久类的类全称。

第一个column:保存上面value值的字段。

第二个column:保存引用持久类的主键值的字段,它的类型是id-type对应到数据库中的字段类型。

3、记录日志的方法:
public MyLog recordLog(Object biz);{   

    MyLog log = new MyLog();;   


    log.setLogDate(new Date(););;   


    log.setLogEntity(biz);; //引用了传递过来的业务对象   

  

    return getLogService();.save(log);; //保存log。我习惯用Spring+Hibernate。   

}  
public MyLog recordLog(Object biz);{
	MyLog log = new MyLog();;
	log.setLogDate(new Date(););;
	log.setLogEntity(biz);;	//引用了传递过来的业务对象

	return getLogService();.save(log);;	//保存log。我习惯用Spring+Hibernate。
}

Hibernate所保存的数据是这样:

引用

--  ---------------------------------  ---------------   --------

id  logDate           entityMetaValue entityId

--  ---------------------------------  ---------------   --------

1  2004-11-1520:48:52.211  One        1

2  2004-11-1520:57:25.385  Two        2

3  2004-11-1521:48:52.211  One        15

4  2004-11-1522:51:15.185  Two        26

5  2004-11-1523:27:55.123  Two        36

4、读取Log

public MyLog readLog(Long id);{   
    MyLog log = getLogService();.getLog(id);;   
  
    Object biz = log.getLogEntity();;   

    //...   

  

    return log;   

}  
public MyLog readLog(Long id);{
	MyLog log = getLogService();.getLog(id);;

	Object biz = log.getLogEntity();;
	//...

	return log;
}

用<any>所实现的关联,与<many-to-one>等关联的效果是相同的。例如,如果BizOne,BizTwo的lazy="true",则biz是个代理。

5、BizThree如果增加了一个业务类BizThree,在MyLog.hbm.xml中只需增加一行:

<meta-value value="Three" class="com.test.entity.BizThree"/>  
<meta-value value="Three" class="com.test.entity.BizThree"/>

6、限制

在<any>元素中需要指定id-type,这可能是<any>对所关联类的唯一限制了:所关联的类的主键类型必须相同。

三、再谈继承映射策略问题

上面提到了:如果为了让Log能够关联业务类,就要求业务类都要继承同一个超类,是不大合适的。不过,不合适的理由在于这个超类需要在数据库有相应的表。不能说,业务类不能继承一个超类。实际上,很多应用中的业务类都有超类,而且根据情况实现一些接口。此时的继承映射策略是HibernateReference中的第三种:每个具体类一个表(tableperconcreteclass)。上面MyLog中的logEntity的类型可以是更有意义的超类,如Entity,当然也可以是接口,不必是Object。这样,即使超类在数据库中没有对应的表,照样可以实现关联。

四、彩票

HibernateReference中提到<any>元素的地方不是很多,但提到时,总不忘记说“应该很少用到”,“应该在非常特殊的情况下使用它”。可能从全世界的角度看,使用<any>是低概率事件,但是如果遇到了,就是100%的概率了。因此,当你应用<any>的时候,别忘了购买彩票。因为,与中奖同样的低概率事件——使用<any>——你已经碰到了,你的运气就来了,赶快买彩票吧,准能中奖!

五、感谢

感谢这个论坛!

感谢Hibernate相关资料的译作者,尤其是HibernateReference!

如果我所写的有不对的地方,请指出来。感谢!

相关推荐