上一篇花了很长时间梳理了下SessionImplementor接口与Hibernate里其它关键类和接口的关系并画出类图,最后得出"集万千宠爱于一身的SessionImpl"标题.本篇将接着往下走,下面先看第二句 EntityPersister persister;
声明了一个变量persister.(写到这有两种选择,一是像对SessionImplementor一样对这个EntityPersister接口好好研究一番,这样的话研究的很细,不过一个突出的问题就是由get方法牵连出很多点,分散精力;另一选择是把这个EntityPersister放在当前DefaultLoadEventListener类里结合它的具体应用来有个初步的认识. ... 现在尝试第二种选择,由于对SessionImplementor的研究是用第一种方法来展开的,看看这第二种选择有什么优缺点,再进一步来看如何与第一种结合起来以达到优势互补.)
有了上面的研究策略,这样把DefaultLoadEventListener类里的第62行至第76行拿出来做为一个意群(借用高考语文阅读训练中的一个术语)整体研究.
先看persister这个变量的声明方式,这里在第62行并没有对其赋值,结合项目中的一些经验,这样的声明是有一定的风险的,处理不好就报NullPointerException了.
先说说项目中遇到的类似情况及是怎么报异常的:
这样的一个异常容易发生在MVC端,
假设在C端有如下的处理逻辑:
Result result;
if(parameterConditionOne){
result = conditionOneProgram.getResult();
}else if (parameterConditionTwo){
result = conditionTwoProgram.getResult();
}
.................
result.setXXX(); // --- A
看上面的代码,若两个条件都不满足的话,在A处就会报NullPointerException,原因就在于分析情况时没有考虑全就造成现在这样的漏网之鱼.若在else if后再加一个else分枝呢?这样也是有值得注意的地方的,是否在非parameterConditionOne非parameterConditionTwo时就一定是else里的情况呢?
看Hibernate是怎么处理类似的麻烦事的:
在源码里71至76行的判断及异常处理.也就是说在经过if...else的赋值后再看这个persister是否这空,若为空就抛出一个HibernateException,这样把NullPointerException这样很唐突很底层的异常转为更为specific地异常.
现在看了Hibernate这里的处理方法后,佩服Hibernate的同时,也感觉到自己的成长,这样也就更加坚定了自己研究源码.
上面这段让我想起了自己项目中的一个Bug,现在再回到Hibernate的处理逻辑中.
第一个分支,即:event.getInstanceToLoad() != null 也就是说若event在初始化时传入并设置了instanceToLoad属性,这当然不是get方法时的调用情况了,BTW:Session接口中的load(Object object, Serializable id)方法就是这样的情况:在初始化LoadEvent事例时把load方法传入的Object对象再次传给LoadEvent的构造方法,从而就赋给了instanceToLoad属性.
若是这样的已初始化了instanceToLoad属性,persister的初始化就不同了,即通过source.getEntityPersister来返回,这样好像也能说的过去,毕竟那个instance已经在persistenceContext里存在了,这个instance也已经绑定了与之对应的EntityPersister了,也就不用再new一个EntityPersister的实现类对象了.在这里需要做的就是把event里的EntityClassName赋下值,现在的猜想是若传入了instanceToLoad就没有再传相应类的名字.这也很好理解,毕竟一个instanceToLoad本身就已经带了很丰富的内容.
source.getEntityPersiter()
与event.getInstanceToLoad() != null相反的是没有传入instanceToLoad对象,这种情况下我猜想是Hibernate在此时利用如下语句new出来一个与当前EntityClassName对应的EntityPersister实现类对象.
persister = source.getFactory().getEntityPersister( event.getEntityClassName());
追着源码看,并没有找到我猜想的那个new,那两种getEntityPersister有什么区别?也就是说若instanceToLoad不为null时的source.getEntityPersister与instanceToLoad为null时的source.getFactory().getEntityPersister两种有什么不同?这样就落在getFactory有什么用?
...............
从表面上来看是落在getFactory的作用上,但若往深入地看时发现,source.getEntityPersister实质上也是调用getFactory,而真正不同的是,这个方法中又调用了SessionImpl类里的guessEntityName方法,顾名思义,就是根据传入的Object对象来猜出这个Object在Hibernate里的Entity名字,不过还想往下问的是这样guess出来的名字与利用Object的反射得到的className有什么不同?毕竟LoadEvent里的getEntityClassName就是通过Class类的getName()方法获得的.这个问题先放这吧.
接着往下走,就是看ID是用什么类来表示的?是Long?是Integer?是String?并把这个类型与xxx.hbm.xml中的配置的类型做比较,若不符的话就抛出TypeMismatchException,我觉得这样的比较意义倒不是很大,或者说我不大理解为什么要这样比较?像一个Long型的id若用一个int型的值来表达时也不是不可以的吧?当然只要那个实际的值别超过int类型的范围,不过不理解归不理解,在使用时还是一定注意这个问题的.
这里还有一个问题:
persister.getIdentifierType().isComponentType() && EntityMode.DOM4J == event.getSession().getEntityMode()
怎么上面这个条件成立时就不用来比较了呢?当然若是componentType的话就没有明显的一个className了,这倒是一个原因,但这与EntityMode.DOM4J又有什么关系呢?这个问题还是只能先放这了.
下面又有一个新类EntityKey,不禁要问这个类是干啥的?为什么叫EntityKey呢?这样这个onLoad方法在每次调用时都new出来一个这样的对象是不是很是浪费?好像是JVM在每new一个对象时很耗资源的.能不能像Spring所提倡的那样最好用单例?又是什么因素制约着不能用单例呢?带着这些问题看EntityKey这个类的源码.
看这个类的源码发现与其它的类并没什么特殊的联系.自身除了三个方法外,也没有特别的.
这三个方法分别为private的generateHashCode与访问修饰为默认的serialize还有一个static且返回值为Entity的deserialize方法.这个deserialize方法没什么可说的,它自身就是一个工厂方法,返回一个EntityKey对象.
先看generateHashCode方法,这个方法也只是在EntityKey的构造方法中调用一次,对传入构造方法里的各个参数分别调用它们的hashCode方法并最终拼出一值赋给EntityKey的属性hashCode,这样也就能多多少少地了为什么叫key了.那这个key又具体怎么用呢?只能在它的具体应用中来看了.这让我想起了现在项目中通过字符串的方式拼出来的值作为自制缓存中key来使用了,为什么不用类的hasCode呢?那样的话显得的更专业.
再看serialize这个方法,它将EntityKey里的属性通过writeObject方法写进传入的ObjectOutputStream参数中?为什么要这样呢?这个serialize方法在什么情况下调用?想达到什么效果?是要写入本地文件中吗?追着看了看没看出什么门道,就先放在这吧.
从EntityKey返回到DefaultLoadEventListener类里的onLoad方法.下面将根据loadType是否为isNakedEntityReturned与LockMode是否为LockMode.NONE来调用不同的load方法,这里有三个不同的方法,依次是load,proxyOrLoad,lockAndLoad.这三个方法中除了第三个方法lockAndLoad多一个SessionImplementor型的参数外,都接收前面准备好的或onLoad传来的四个参数:event, persister, keyToLoad, loadType.
大致看了下这三个方法,proxyOrLoad,lockAndLoad这两人都有视情况不同而调用load方法.这样就先研究这个load方法了.
今天写了不少,虽说是有些乱.在下一篇中将重点看这个load方法.
分享到:
相关推荐
hibernate源码
标题"hibernate源码 直接使用"表明我们将探讨的是Hibernate框架的源代码,以及如何直接在项目中应用这些源代码。Hibernate是一个流行的Java ORM(对象关系映射)框架,它简化了数据库操作,将数据库交互转化为面向...
接下来,我们将深入探讨Hibernate的核心概念、如何在Eclipse中导入源码以及如何利用这些源码进行学习。 1. Hibernate 核心概念: - ORM(Object-Relational Mapping):ORM是将数据库中的关系数据映射为Java对象的...
在深入研究Hibernate源码时,可以关注以下关键点: - **实体管理**:了解实体生命周期(瞬态、持久化、托管和脱管),以及状态转换的过程。 - **HQL和Criteria API**:学习如何构建面向对象的查询,以及它们与原生...
《Hibernate源码解析(一)》 在Java开发领域,Hibernate作为一款强大的对象关系映射(ORM)框架,极大地简化了数据库操作。深入理解Hibernate的源码,不仅可以帮助开发者更好地运用该工具,还能提升对Java编程和...
Hibernate源码(hibernate-orm-main.zip)Source Code: Hibernate ORM 是一个为应用程序、库和框架提供对象/关系映射 (ORM) 支持的库。 它还提供了 JPA 规范的实现,这是 ORM 的标准 Java 规范。
**标题与描述解析** 标题"传智播客hibernate源码"暗示了这是一个关于Hibernate框架...通过研究这些文件,学习者可以深入了解Hibernate的工作原理,提升数据库操作的技能,并了解如何在实际项目中有效地使用Hibernate。
《深入剖析Hibernate源码》 Hibernate,作为一款广泛使用的开源对象关系映射(ORM)框架,为...通过对源码的深入研究,我们可以更好地利用Hibernate提供的功能,优化我们的应用,同时也能为自己的项目设计提供灵感。
《精通Hibernate源码》 ...通过深入研究Hibernate源码,我们可以更好地理解ORM的工作原理,从而在项目实践中更好地运用Hibernate,提高代码质量和开发效率。同时,掌握源码也有助于定制化开发,满足特定业务需求。
在本篇《Hibernate源码解析(二)》中,我们将深入探讨Hibernate这一强大的Java对象关系映射(ORM)框架的内部工作原理。这篇博客旨在帮助开发者更好地理解Hibernate的核心机制,以便于更高效地利用它来处理数据库...
Hibernate是一个开源的对象关系映射(ORM)框架,它允许开发者使用Java对象来操作数据库,而无需直接编写SQL语句。...通过对"hibernate-one2many"的分析,我们可以深入研究和实践一对一和一对多关联关系的配置和操作。
总之,深入学习Struts、Spring和Hibernate的源码,对于提升Java开发者的专业水平具有重要意义。这不仅涉及到了Web开发的基本原理,还涵盖了软件设计、框架构建和数据库交互等多方面的知识。通过这种方式,开发者可以...
Hibernate是一种Java语言下的对象关系映射解决方案。 它是使用GNU宽通用公共许可证发行的自由、开源的软件。它为面向对象的领域模型到传统的关系型数据库的映射,提供了一个使用方便的框架。Hibernate也是目前Java...
《深入理解Hibernate源码》 Hibernate,作为Java领域中的一款著名持久化框架,极大地简化了数据库操作,使得开发者能够更加专注于业务逻辑。本资料主要基于“传智播客hibernate源码.rar”进行深入剖析,旨在帮助你...
《Hibernate源码解析(三)》这篇文章主要探讨了Hibernate框架的深入源码解析,这是对 Hibernate 框架理解的进一步深化。在本篇中,作者聚焦于几个关键的组件和机制,帮助读者理解 Hibernate 如何高效地管理和操作...
《深入剖析Hibernate 4.1.4.Final源码》 Hibernate,作为一款著名的Java对象关系映射(ORM)框架,极大地简化了Java开发者在数据库操作中的工作。它通过提供对象化的数据访问接口,使得开发者可以更加专注于业务...