`
icewubin
  • 浏览: 34874 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Hibernate 3.2中annotation注释的位置对性能的巨大影响

阅读更多
原贴:
http://www.iteye.com/topic/212236

近日把自己的AppFuse改写成JDK 1.5的形式,主要的改动是Hibernate配置改成annotation配置,原本认为很顺利,没想到碰到很多怪问题,不得不多次拿Hibernate源码进行跟踪。

其中一个要命的问题如下:

原本我是用xDoclet2的方式写注释,运行ant生成hbm配置脚本,xDoclet1一定要写在getMethod上,让我很不爽,xDoclet2注释可以写在field上,代码可读性不错,因为field的语义注释一般都是写在field上的,不大会写在getMethod上,不过xDoclet2跑在JDK 1.4下会报错,不过还好不影响生成。

说了这么多废话,其实就是表明原来的方式是运行在hbm配置文件的方式,希望改写成annotation后原有的数据抓取表现行为一致。

先说说JDK 1.4下的情况:
运行环境Hibernate 3.2多对一生成的配置举例,假设是user.hbm.xml(test.User):
<many-to-one column="group_id" name="group" class="test.Group"/>
在这个配置的默认情况下,分析如下代码:
User user = userManager.findByName("diablo3");
Group group = user.getGroup();
group.getGroupId();
group.getName();

首先第二行不会产生select ... from group WHERE group_id=? 的代码。
关键的是第三行也不会产生,直到第四行才会触发这条sql。
这里还有奇怪的现象,如果在Group.java中有如下代码:
public String getGroupIdTest01() {
    return groupId;
}
public String getGroupIdTest02() {
    return this.getGroupId();
}

然后同等条件执行user.getGroupIdTest01()和user.getGroupIdTest02()都会触发sql语句的生成,昏倒啊,暂且作伏笔,先不讨论这个诡异现象。

先介绍这个不触发sql的用途和应用场景。
比如在一个页面上要显示用户列表(不管是写页面方式,还是JSON序列化都一样),最后一列有一个按钮,按钮上的文字是“显示所属小组详细信息”,那这个按钮触发的事件可能是个弹出窗口或者URL跳转,一般要带个参数groupId=xxx来定位要显示的小组ID,此时数据抓取深度显然不需要group的其他信息,如果触发上面提到的sql,势必产生N+1的性能问题(这也是标题中“巨大”的由来),其实如果事先知道要小组的其它信息,这个页面显示的做法就完全是其他策略了。也就是说,确保调用user.getGroupId()不会产生新的sql是很重要的,能减少很多麻烦。


然后说说JDK 1.5下,当我写新的例子:
@Id
@Column(name = "group_id")
private String groupId;

我为了确保原先的策略不会有问题,特地又做了个实验结果,结果发现,当调用user.getGroup().getGroupId()的时候触发了sql,昏倒中,咋办呀,怎么会这样呢?唉,郁闷几分钟后,把hibernate源码复制进工程中开始调试。

一上来就直奔interface LazyInitializer去了,多半就是这里搞的鬼,大家可以先用debug模式跟一下,会发现CGLIB增强的model中有一个LazyInitializer的对象,所以很自然的ctrl+t,看LazyInitializer的实现类有哪几个,基本锁定CGLIBLazyInitializer,考察其中的invoke方法,跟踪到org.hibernate.proxy.pojo.BasicLazyInitializer,其中有如下一方法:
protected final Object invoke(Method method, Object[] args, Object proxy)
基本知道这就是调用group.getGroupId()在运行时调用的代理方法了,找到其中一段话:
else if ( isUninitialized() && method.equals(getIdentifierMethod) ) {
    return getIdentifier();
}

哦,原来是判断,“当前代理对象数据未加载”且“调用的方法就是id的getMethod”,满足此判断的话,返回直接能够直接获取id的Method。也就是说,这就是不触发sql产生的实现代码的所在地,代理对象此时肯定已经有id,没必要再去数据库读。之前说的“诡异现象”,看了这段代码就明白了,只有id的getMethod才不会触发sql。

加了打印语句,发现新的实验中getIdentifierMethod为空,导致这个条件判断没有满足,跳过去了。
然后接着继续跟踪为什么getIdentifierMethod是null,先跟到CGLIBLazyInitializer的private构造方法,然后再找到CGLIBProxyFactory的postInstantiate方法,哦getIdentifierMethod是传进来的。

此时有点棘手了,这个类有三个地方引用,定位有点麻烦,花了15分钟,反复调试,定位到org.hibernate.tuple.entity.PojoEntityTuplizer的buildProxyFactory方法,发现此方法的传入参数Getter idGetter是导致getIdentifierMethod为空的元凶,打印了一下,发现传入的Getter类形是org.hibernate.property.DirectPropertyAccessor,此时我没有再跟,稍微想了下,基本知道了,我马上把annotation位置换了下,放在getGroupId方法之前:
@Id
@Column(name = "group_id")
public String getGroupId() {
    return groupId;
}

问题解决,后来查了资料确认了情况,原来annotation写在field上,hibenate默认就是认为是访问方式是“field”,如果写在getMethod上,访问方式是“property”。访问方式是“field”的话,就会拿不到之前说的“idGetter”,访问方式是“property”的话就能拿到,这样在调用代理对象的getGroupId()方法的时候getIdentifierMethod不为空,就能通过判断,hibernate发现,哦,你是在调id的getMethod,那就把ID直接给你吧。其实还是hibernate不够智能,应该支持直接通过method的名称,倒推出field的名字,当然这个method必须满足getMethod的特性(返回类型和id的field类型一致,且这个方法没有参数),判断出这个field的名字和id的名字相等,就认为是在调用id的getMehtod就可以了嘛。

新的问题是我又不想把annotation写在getMethod上,咋办呢?改写如下就行了:
@Id
@AccessType(value = "property")//注意这里
@Column(name = "group_id")//实际做的时候没有这一行,用了其他技巧自动转换名字为group_id
private String groupId;

只要ID这么写就行了,别的属性可以不写,官方文档中说不能混用field读取方式和property方式,不用理他的。

至此,问题解决,希望对大家有帮助。

稍微总结一下:
1.传统hbm配置文件和annotation的默认访问不一样,写注释的时候ID要指定用property方式。
2.只有ID用property方式下,在特定场景下,hibernate才能找到getIdentifierMethod,才能直接返回id,不会触发sql。
3.这个不触发sql的特性本身,大家也应该多多利用,思维方式和传统sql编程方式比较接近:只需要这个外键ID,不需要其他表中的详细数据。
10
1
分享到:
评论
1 楼 simple1024 2011-12-21  
annotation用起来感觉非常舒服。
反而自己搭建用xml配置时纠结的要死。一直还想着是否再换成annotation

这个位置确实是有讲究。

受教了,冰大哥。

呵呵,因为你一直钻研DIY的呢。现在发现同行啊。

相关推荐

    hibernate3.2 chm 帮助文档

    《Hibernate 3.2 帮助文档》是针对Java开发者的重要参考资料,它详细阐述了Hibernate 3.2版本的框架特性和使用方法。Hibernate是一个开源的Java对象关系映射(ORM)框架,它简化了Java应用与数据库之间的交互,通过...

    struts2.1 + spring 2.5 + hibernate 3.2 增删改查

    在Hibernate中,我们定义实体类(Entity)和映射文件(Hibernate Mapping File或Annotation),以描述数据库表结构。通过SessionFactory和Session接口,我们可以执行CRUD操作,而无需编写大量SQL语句。 在这个例子...

    Hibernate-Annotation中文教程.pdf

    传统上,Hibernate 的配置依赖于外部 XML 文件,而最近发布的几个 Hibernate 版本中,出现了一种基于 Java 5 注释的更为巧妙的新方法,即 Hibernate Annotation 库。借助新的 Hibernate Annotation 库,即可一次性地...

    struts2+spring2.5+hibernate3.2 annotation配置完整eclipse项目,带数据库脚本

    Struts2、Spring2.5和Hibernate3.2是经典的Java Web开发框架组合,它们各自在应用程序的不同层面提供了强大的功能。这个"sshTest"项目是一个使用这三个框架的注解配置的完整Eclipse工程,同时也包含了数据库脚本,...

    hibernate3.2

    9. **性能优化**:在3.2版本中,Hibernate对SQL生成进行了优化,减少了不必要的数据库交互,提高了整体性能。 10. **XML和 Annotation 配置**:Hibernate 3.2支持XML配置文件和注解两种方式来定义对象关系映射,...

    spring2.5+hibernate3.2

    spring2.5 + hibernate3.2x 标注(annotation)开发的简单示例 http://blog.csdn.net/IamHades/archive/2008/01/11/2038188.aspx

    Hibernate3.2帮助文档.chm格式

    8. **Criteria API与JPA Criteria API**:虽然两者名字相似,但在Hibernate中,JPA Criteria API是按照Java Persistence API标准实现的,而Hibernate Criteria API是其特有功能,两者可以结合使用。 9. **二级缓存*...

    Hibernate distribution and annotation

    在这个压缩包中,我们有两个主要的部分:Hibernate 3.3.2和Hibernate Annotation 3.3.4。 **一、Hibernate 3.3.2** Hibernate 3.3.2是该框架的一个稳定版本,发布于2009年。这个版本包含了对JPA(Java Persistence ...

    hibernate3.2官方源代码

    通过深入研究Hibernate 3.2的源代码,开发者可以更好地了解ORM的实现细节,提升项目性能,解决复杂问题,同时为自定义扩展或开发类似框架打下基础。尽管3.2版本相对较旧,但其设计理念和实现方式对于理解现代ORM仍有...

    hibernate3.2中文文档(chm格式)

    HIBERNATE - 符合Java习惯的关系数据库持久化 Hibernate参考文档 3.2 -------------------------------------------------------------------------------- 目录 前言 1. 翻译说明 2. 版权声明 1. Hibernate...

    hibernate annotation中文文档

    hibernate annotation中文文档

    Hibernate-Annotation中文教程.docx

    借助新的 Hibernate Annotation 库,即可一次性地分配所有旧映射文件——一切都会按照您的想法来定义——注释直接嵌入到您的 Java 类中,并提供一种强大及灵活的方法来声明持久性映射。 使用 Hibernate 注解的优点...

    hibernate annotation 中文文档

    《Hibernate Annotation 中文文档》是Java开发者们的重要参考资料,它详细介绍了如何在Hibernate框架中使用注解进行对象关系映射(ORM)。Hibernate是一款强大的开源Java持久化框架,它简化了数据库与Java对象之间的...

    spring2.5+hibernate3.2+struts2.0组合配置说明

    在当前的软件开发领域,Spring2.5、Hibernate3.2 和 Struts2.0 这三个框架因其卓越的性能与丰富的功能而备受青睐。它们分别在业务逻辑层管理、数据持久化以及MVC模式实现方面提供了强大的支持。下面将详细介绍如何...

    hibernate中文APIhibernate annotation 中文API

    Hibernate是Java开发中的一个流行对象关系映射(ORM)框架,它允许开发者用面向对象的方式操作数据库。Hibernate Annotation则是Hibernate的一部分,它引入了Java注解,使得配置更加简洁,无需XML文件即可进行对象...

    spring+hibernate3.2+struts2.0 注解

    在Hibernate 3.2版本中,注解支持已经相当成熟,可以完全摆脱hbm.xml映射文件,直接在实体类上使用注解进行配置。例如,`@Entity`表示这是一个数据库表对应的实体类,`@Table`指定表名,`@Id`定义主键,`@...

    Hibernate Annotation 中文文档

    以上是对Hibernate Annotation的简要介绍,深入理解和熟练应用这些注解,将有助于提升你在Java持久化领域的专业技能。对于更详细的用法和高级特性,建议查阅《Hibernate Annotations参考文档-3.20》这份资料,以获取...

    hibernate 注解 annotation 教程

    hibernate 注解 annotation 教程

    Hibernate Annotation jar

    这里面包涵了需要用Hibernate Annotation时,所需要的所有jar包! 现在我们公司在做web项目的时候,已经不用*.hbm.xml这种映射文件了,都是用Annotation(注解)方式来完成实体与表之间的映射关系,这样看起来比用...

    hibernate annotation hibernate3

    Hibernate 3是Hibernate ORM框架的一个重要版本,它引入了许多新特性,如对JPA(Java Persistence API)的支持,以及对注解的广泛使用。这一版本的更新使得Hibernate更加易于使用,同时也提高了代码的可读性和可维护...

Global site tag (gtag.js) - Google Analytics