论坛首页 Java企业应用论坛

Hibernate中any元素的应用体会

浏览 18592 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-11-17  
Hibernate中any元素的应用体会


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

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

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

在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、“每棵类继承树使用一个表(table per class hierarchy) ”
2、“每个子类一个表(table per subclass)”

第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 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>


表结构(MySQL):
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。
}


Hibernate所保存的数据是这样:
引用

--  ---------------------------------  ---------------   --------
id  logDate           entityMetaValue entityId
--  ---------------------------------  ---------------   --------
1  2004-11-15 20:48:52.211  One        1
2  2004-11-15 20:57:25.385  Two        2
3  2004-11-15 21:48:52.211  One        15
4  2004-11-15 22:51:15.185  Two        26
5  2004-11-15 23:27:55.123  Two        36

4、读取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"/>


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

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

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

五、感谢
感谢这个论坛!
感谢Hibernate相关资料的译作者,尤其是Hibernate Reference!

如果我所写的有不对的地方,请指出来。感谢!
   发表时间:2004-11-17  
发新帖时,好像“预览”功能不是很好用?
0 请登录后投票
   发表时间:2004-11-18  
灌点水。
    其实我觉得Any应用非常的广泛,比如,某种单据的来源可能会是很多不同的单据,这个时候就是用Any的时候。最简单的例子,如在企业财务中汇款记录,其产生汇款单的原因可能是罚款单、员工工资、采购付款等!当然说到这可能有人就会说,只要在罚款单、工资单、付款单等上面记录下汇款单的ID就可以了,但当老板突然问你这个汇款单对应的业务是什么的时候,你就得去从罚款单、工资单、付款单去找很久,希望你的老板能有足够的耐心让你找!哈哈!
    有了Any我们能将很多以前很难处理的关联关系记录下来,同时也非常有利于处理类似要在单据中记录单据产生来源的这种情况!
    但怎么说呢,说来还有点缺点就是,每次增加一种单据来源,这个时候都必须重新编写hbm文件,系统的动态配置性差了那么一点点。如果在其Meta value中自动保存为完整的Class名称,Hibernat在实例化的时候也自动以Meta value中所记录的这个Class名称来获取一个类来进行实例就好了!这样配置和使用的方便性都会有很大的提供。
0 请登录后投票
   发表时间:2004-11-19  
LonsenLuo 写道
灌点水。
    其实我觉得Any应用非常的广泛,...

我也有同感。我现在所做的项目,如果没有Any功能,就需要自行编写类似的代码。Any的资料好像很少,我在google上粗略搜索一下,没找到有价值的文章。联想到Hibernate Reference中多次提及“很少使用”字样,这样看来,可能应用不多吧。
    今天,找到一个知音。btw,你买彩票没有?

LonsenLuo 写道
最简单的例子,如在企业财务中汇款记录,其产生汇款单的原因可能是罚款单、员工工资、采购付款等!当然说到这可能有人就会说,只要在罚款单、工资单、付款单等上面记录下汇款单的ID就可以了,但当老板突然问你这个汇款单对应的业务是什么的时候,你就得去从罚款单、工资单、付款单去找很久,希望你的老板能有足够的耐心让你找!哈哈!

这个例子好啊,有类似应用的朋友可以借鉴一下。

我在上面提到的Log功能,并不是我所做项目需要的。只是举具体项目的例子,不容易编写示例代码。实际上,我的具体应用是工作流和附件管理。应用中的一般只会有一个工作流引擎,关联各种业务实体就需要any元素。附件管理也是一样。
0 请登录后投票
   发表时间:2005-03-18  
但是好像有个问题就是,无法建立反向关联,就是从LogEntity去获得对应的MyLog时,将会得到错误的MyLog
0 请登录后投票
   发表时间:2005-08-12  
LonsenLuo 写道
灌点水。
    但怎么说呢,说来还有点缺点就是,每次增加一种单据来源,这个时候都必须重新编写hbm文件,系统的动态配置性差了那么一点点。如果在其Meta value中自动保存为完整的Class名称,Hibernat在实例化的时候也自动以Meta value中所记录的这个Class名称来获取一个类来进行实例就好了!这样配置和使用的方便性都会有很大的提供。


只要你不在mapping file中指定mata value , hibernate保存的就是class的完整名称。但如此一来,将会为重构带来很大的问题。
0 请登录后投票
   发表时间:2006-10-23  
关于any的查询我有个问题,

我要根据这个影射的字段为条件进行查询。结果抱错,说是找不到属性。
org.hibernate.QueryException: could not resolve property: container of:

影射文件:
<any name="container" id-type="int" meta-type="int">
    <meta-value value="1" class="oss.rms.inventory.instance.node.domain.Card"/>
    <meta-value value="2" class="oss.rms.inventory.instance.node.domain.Shelf"/>
    <meta-value value="3" class="oss.rms.inventory.instance.node.domain.Device"/>
    <column name="CONTAINER_TYPE"/>
    <column name="CONTAINER_ID"/>
</any>

查询语句:

String hql = "from Port p where p.container = ?";
Object[] objects = new Object[]{obj};
return (ArrayList <Port> ) this.getHibernateTemplate().find(hql, objects);

异常:
org.hibernate.QueryException: could not resolve property: container of: xxx
谁能帮我看看,这个是为什么啊.... 
0 请登录后投票
   发表时间:2006-10-27  
不可以直接用Any的属性名进行HQL查询,需要对你需要用的属性另外配置简单的POJO属性。
0 请登录后投票
   发表时间:2007-01-24  
JohnLee 写道
不可以直接用Any的属性名进行HQL查询,需要对你需要用的属性另外配置简单的POJO属性。

具体一点啊。
我是先把model修改的。增加相应的属性。
比如CONTAINER_TYPE,CONTAINER_ID的geter,seter
再把model相对应的hbm.xml文件也修改.
这种方法的缺点是,用hbm.xml文件生成数据库的时候会有小问题。
因为我是用xml文件来生成数据库库的,这样的话表会多出两列相同名字的字段。
0 请登录后投票
   发表时间:2007-07-25  

对于any的查询,我遇到一个问题,我的映射文件如下,rich editor似乎有点问题(遇到好几次了),

xml 代码
  1. <class name="TPrettyBack" table="cn_com_prettystory_back_TPrettyBack">  
  2.   <id name="id" type="integer" column="id">  
  3.     <generator class="native"/>  
  4.   </id>  
  5.   <property name="name" column="name" type="string" not-null="false" length="50"/>  
  6.   <property name="content" column="cotent" type="text"/>  
  7.   <any name="owner" meta-type="string" id-type="integer" cascade="none">  
  8.     <meta-value value="product" class="cn.com.prettystory.product.TPrettyProduct"/>  
  9.     <meta-value value="enterprise" class="cn.com.prettystory.enterprise.TPrettyEnterprise"/>  
  10.     <column name="taskOwnerType"/>  
  11.     <column name="ownerId"/>  
  12.   </any>  
  13. </class>  


我需要根据meta-value的值取的所有的列,比如,我需要取得数据库中taskOwnerType='product'的所有列,这样的Hql该如何写?

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics