`

从源码层面来看load与get的区别(五): Hibernate源码研究碎得(9)

阅读更多

上一篇花了很长时间梳理了下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方法.

2
1
分享到:
评论
3 楼 rmn190 2009-02-22  
rmn190 写道

1, "那两种getEntityPersister有什么区别?"   现在感觉第一种方式包含在第二种里了, 我们做下的猜想: 第二种通过EntityClassName从那个factory中以Map的key方式取出EntityPersister, 也就是说比第一种多了个map的get()操作.2, "新类EntityKey,不禁要问这个类是干啥的?"    回过头来看, 觉得这个类是当把load来的东西放到缓存时的Key.



说是放到缓存时的Key不够确切, 应该说是放到当前Session缓存中的Key, 与二级缓存没关系, 想想也是二级缓存是Hibernate的调用者选择配置的, Hibernate自身怎么回"干涉"二级缓存中的事呢?
2 楼 rmn190 2009-02-21  
1, "那两种getEntityPersister有什么区别?"
   现在感觉第一种方式包含在第二种里了, 我们做下的猜想: 第二种通过EntityClassName从那个factory中以Map的key方式取出EntityPersister, 也就是说比第一种多了个map的get()操作.

2, "新类EntityKey,不禁要问这个类是干啥的?"
    回过头来看, 觉得这个类是当把load来的东西放到缓存时的Key.

1 楼 rmn190 2009-02-21  
"若为空就抛出一个HibernateException,这样把NullPointerException这样很唐突很底层的异常转为更为specific地异常":

    不仅仅是一个自己项目中那样的NullPointerException问题,这时还包括在那种情况下都没有得到实例的情况,hibernate已经通过if... else把漏网之鱼堵住了,本质上跟自己项目中的情况大不一样.

相关推荐

    hibernate 三种 查询 方式 load与get的区别

    NULL 博文链接:https://cici9898-007.iteye.com/blog/1847710

    hibernate中get和load方法的区别

    NULL 博文链接:https://ysj5125094.iteye.com/blog/1897557

    Hibernate的get和load方法的区别

    NULL 博文链接:https://811417208-qq-com.iteye.com/blog/506950

    传智播客李勇hibernate源码1-20课

    传智播客李勇hibernate源码1-20课,目录如下:01_hibernate介绍与动手入门体验;02_hibernate入门案例的细节分析; 03_hibernate入门案例的代码优化; 04_Session接口及get|load|persist方法 05_实体对象的三种状态...

    Hibernate查询 load与get的区别及其它查询测试

    NULL 博文链接:https://quicker.iteye.com/blog/658451

    hibernate get/load/saveOrUpdate/statistics

    博文链接:https://log-cd.iteye.com/blog/205166

    spring_MVC源码

    15. <load-on-startup>1</load-on-startup> 16. </servlet> 17. <servlet-mapping> 18. <servlet-name>spring</servlet-name> <!-- 这里在配成spring,下边也要写一个名为spring-servlet.xml的文件,主要用来...

    ssh(structs,spring,hibernate)框架中的上传下载

     需要指定的是Spring 1.2.5提供了两套Hibernate的支持包,其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中,而Hibernate 3.0的封装类位于org.springframework.orm.hibernate3.*包中,...

    DWR.xml配置文件说明书(含源码)

    因为指定属性转换只有针对bean才有效(基本类型的转换没有必要指定属性),这个功能只能应用与特定的converter即BeanConverter和从次类派生的所有类.相关配置语法如下 , property2"/> 这样就可以限定DWR不能调用Fred...

    springmybatis

    mybatis实战教程mybatis in action之五与spring3集成附源码 mybatis实战教程mybatis in action之六与Spring MVC 的集成 mybatis实战教程mybatis in action之七实现mybatis分页源码下载 mybatis实战教程mybatis in ...

    cms后台管理

    解压缩jeecms-3.0.2-final-src,在src文件夹下会看到有三个压缩文件,如果只想进行普通的二次开发,可以只导入cms这个源码,删除jeecms-cms-identity-3.0.2-final.jar即可,如果想进行深入的二次开发,需要导入...

Global site tag (gtag.js) - Google Analytics