Hibernate中any元素的应用体会

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

How to use Hibernate @Any-related annotations?

http://stackoverflow.com/questions/217831/how-to-use-hibernate-any-related-annotations

hibernate annotation 使用@Any 跟下面等效果

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、类:

业务类:

Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://www.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20BizOne%20%7B%0A%09private%20Long%09id%3B%0A%09private%20String%09bizOneDescription%3B%0A%09%2F%2FGetters%20and%20Setters%20%E7%9C%81%E7%95%A5%0A%7D%0Apublic%20class%20BizTwo%20%7B%0A%09private%20Long%09id%3B%0A%09private%20String%09bizTwoDescription%3B%0A%09private%20Date%20%20%20%20createDate%3B%0A%09%2F%2FGetters%20and%20Setters%20%E7%9C%81%E7%95%A5%0A%7D" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. public   class  BizOne {  
  2.     private  Long    id;  
  3.     private  String  bizOneDescription;  
  4.     //Getters and Setters 省略   
  5. }  
  6. public   class  BizTwo {  
  7.     private  Long    id;  
  8.     private  String  bizTwoDescription;  
  9.     private  Date    createDate;  
  10.     //Getters and Setters 省略   
  11. }  
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 省略
}



日志类:

Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://www.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20class%20MyLog%20%7B%0A%09private%20Long%09id%3B%0A%09private%20Date%09logDate%3B%0A%09private%20Object%09logEntity%3B%09%2F%2F%E8%BF%99%E5%B0%B1%E6%98%AF%3Cany%3E%E5%85%83%E7%B4%A0%E5%AF%B9%E5%BA%94%E7%9A%84%E5%B1%9E%E6%80%A7%E3%80%82%0A%09%2F%2FGetters%20and%20Setter%20%E7%9C%81%E7%95%A5%0A%7D" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. public   class  MyLog {  
  2.     private  Long    id;  
  3.     private  Date    logDate;  
  4.     private  Object  logEntity;   //这就是<any>元素对应的属性。   
  5.     //Getters and Setter 省略   
  6. }  
public class MyLog {
	private Long	id;
	private Date	logDate;
	private Object	logEntity;	//这就是<any>元素对应的属性。
	//Getters and Setter 省略
}



2、hbm.xml 和 表结构:
这里只给出MyLog的hbm.xml(BizOne, BizTwo很简单,不提了):

Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://www.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%3Chibernate-mapping%3E%0A%20%20%20%20%20%3Cclass%20name%3D%22com.test.entity.MyLog%22%20table%3D%22MyLog%22%3E%0A%20%20%20%20%20%20%20%20%3Cid%20name%3D%22id%22%20column%3D%22id%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cgenerator%20class%3D%22native%22%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2Fid%3E%0A%20%20%20%20%20%20%20%20%3Cproperty%20name%3D%22logDate%22%2F%3E%0A%20%20%20%20%20%20%20%20%3Cany%20name%3D%22logEntity%22%20meta-type%3D%22string%22%20id-type%3D%22long%22%3E%0A%20%20%20%20%20%20%20%20%20%20%09%3Cmeta-value%20value%3D%22One%22%20class%3D%22com.test.entity.BizOne%22%2F%3E%0A%20%20%20%20%20%20%20%20%20%20%09%3Cmeta-value%20value%3D%22Two%22%20class%3D%22com.test.entity.BizTwo%22%2F%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Ccolumn%20name%3D%22entityMetaValue%22%20length%3D%2220%22%20%2F%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Ccolumn%20name%3D%22entityId%22%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2Fany%3E%0A%20%20%20%20%3C%2Fclass%3E%0A%3C%2Fhibernate-mapping%3E" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. <hibernate -mapping>  
  2.      <class  name= "com.test.entity.MyLog"  table= "MyLog" >  
  3.         <id name="id"  column= "id" >  
  4.             <generator class = "native" />  
  5.         </id>  
  6.         <property name="logDate" />  
  7.         <any name="logEntity"  meta-type= "string"  id-type= "long" >  
  8.             <meta-value value="One"   class = "com.test.entity.BizOne" />  
  9.             <meta-value value="Two"   class = "com.test.entity.BizTwo" />  
  10.             <column name="entityMetaValue"  length= "20"  />  
  11.             <column name="entityId" />  
  12.         </any>  
  13.     </class >  
  14. </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):

Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://www.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=create%20table%20MyLog%20(%0A%20%20%20id%20BIGINT%20NOT%20NULL%20AUTO_INCREMENT%2C%0A%20%20%20logDate%20datetime%2C%0A%20%20%20entityMetaValue%20VARCHAR(20)%3B%2C%0A%20%20%20entityId%20BIGINT%2C%0A%20%20%20primary%20key%20(id)%3B%0A)%3B" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. create table MyLog (  
  2.    id BIGINT NOT NULL AUTO_INCREMENT,  
  3.    logDate datetime,  
  4.    entityMetaValue VARCHAR(20 );,  
  5.    entityId BIGINT,  
  6.    primary key (id);  
  7. );  
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、记录日志的方法:

Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://www.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20MyLog%20recordLog(Object%20biz)%3B%7B%0A%09MyLog%20log%20%3D%20new%20MyLog()%3B%3B%0A%09log.setLogDate(new%20Date()%3B)%3B%3B%0A%09log.setLogEntity(biz)%3B%3B%09%2F%2F%E5%BC%95%E7%94%A8%E4%BA%86%E4%BC%A0%E9%80%92%E8%BF%87%E6%9D%A5%E7%9A%84%E4%B8%9A%E5%8A%A1%E5%AF%B9%E8%B1%A1%0A%0A%09return%20getLogService()%3B.save(log)%3B%3B%09%2F%2F%E4%BF%9D%E5%AD%98log%E3%80%82%E6%88%91%E4%B9%A0%E6%83%AF%E7%94%A8Spring%2BHibernate%E3%80%82%0A%7D" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. public  MyLog recordLog(Object biz);{  
  2.     MyLog log = new  MyLog();;  
  3.     log.setLogDate(new  Date(););;  
  4.     log.setLogEntity(biz);; //引用了传递过来的业务对象   
  5.   
  6.     return  getLogService();.save(log);;  //保存log。我习惯用Spring+Hibernate   
  7. }  
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

Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://www.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20MyLog%20readLog(Long%20id)%3B%7B%0A%09MyLog%20log%20%3D%20getLogService()%3B.getLog(id)%3B%3B%0A%0A%09Object%20biz%20%3D%20log.getLogEntity()%3B%3B%0A%09%2F%2F...%0A%0A%09return%20log%3B%0A%7D" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. public  MyLog readLog(Long id);{  
  2.     MyLog log = getLogService();.getLog(id);;  
  3.   
  4.     Object biz = log.getLogEntity();;  
  5.     //...   
  6.   
  7.     return  log;  
  8. }  
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中只需增加一行:

Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://www.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%20%20%20%20%20%20%20%20%20%20%09%3Cmeta-value%20value%3D%22Three%22%20class%3D%22com.test.entity.BizThree%22%2F%3E" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. <meta-value value= "Three"   class = "com.test.entity.BizThree" />  
          	<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!

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值