读Hibernate4源码 之一

来源:互联网 发布:英语口语打分软件 编辑:程序博客网 时间:2024/05/07 00:08
什么是OR Mapping框架
主要是实现了程序对象到关系数据库之间的映射
它的实现思想就是将关系数据库中表的数据映射成为对象,以对象的形式展现,这样开发人员就可以把对数据库的操作转化为对这些对象的操作。因此它的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。(来自百度)

由上得知Hibernate ORM是让开发人员操作数据库表就像操作程序中对象一样。
因此可以得到,Hibernate ORM需要解决程序对象与关系数据库表差异,即把数据库表封装成程序对象,总之就是对象与数据库之类一定是建立某种关联,使得操作程序对象的时候能够映射到数据库表。


我们知道,不管是Java EE或是Java SE都可以用Hibernate对吧。为了方便关注到问题点上,我们需要建一个Java SE项目,并其在使用我们的Hibernte框架。读这种比较复杂的框架时,往往需要有两基础条件:1.至少用过这个框架;2.有一个能跑得起来的项目(最好是自己写的,并且简单)。这两个是最基本的条件,才能开始下面阅读的活动。

为什么需要一个跑得起来的项目呢,原因非常简单,由我们阅读的方式所决定,我采用跟读的方式。跟读,即是程序流程一步步往下读。也就是说,我们随着程序流程读。因此,需要程序能通。
跟读,非常适用了解框架的处理流程,也是最为常用的一种方式,方便处理接口编程。比如:
class A implements(或extends) P {public void method(){}}class B implements(或extends) P {public void method(){}}class C {public void method(P p){p.method();}}

面对这种情况,我们是很难知道p到底是谁的实现,给我们阅读代码的过程带来极大的不方便。如果,我们采用Debug跟读,这个问题,就不复存在了。我们也知道,Debug可以看刻下变量的值,也能够帮助我理解代码。

或许你并不知道,Hibernate的代码量有多大。可以明确跟大家说,我们完全没有必要去读Hibernate的全部代码,只需要读自己所关注的部分就可以了。


这里我们统一使用hibernate-release-4.2.3.Final,之所以用Hibernate 4.2.3仅仅只是因为它的源码好找,且是4.0以上。Hibernate-ORM代码分两部分,1:Hibernate-orm;2.Hibernate-commons-annotations。这个地方比较坑爹,以致于我特地拿出来说。坑爹的地方在于,下载下来的hibernate-release-4.2.3.Final.zip这个包里面能找到hibernate-core.jar对应的源码,却找不到hibernate-commons-annotation.jar这一部分的源码。非常之坑。在这里建议大家,在github上下载hibernate-core及hibernate-commons-annotations这两部分代码。然后把hibernate-release-4.2.3.Final.zip里lib/require下除hibernate-core.jar之外都给拷到项目上。这个地方需要大家注意的地方。

然后,我们建一个java project。对了,我这里用的IDE是MyEclipse(Eclipse)。再然后新建一个类里面有一个主方法:
public void main(String[] args){Configuration cfg = new Configuration();cfg.configure(); // = cfg.configure("/hibernate.cfg.xml");SessionFactory sf = cfg.buildSessionFactory();Session session = sf.openSession();}

由上代码可见,Hibernate生命的开始于new Configuration()这个对象创建。之后,我们不难想象,它接下来会做什么事。对一个对象的创建,一般情况下只是做一堆的初始化,对Hibernate来说,也是一样。重要点便落在了 cfg.configure() 上和 cfg.buildSessionFactory() 这两部分上。

显然,cfg.configure() 肯定是做hibernate.cfg.xml这个配置文件的解析。
而 cfg.buildSessionFactory() 必然有绑定了数据库连接,还需要把 POJO 翻译成关系表(a relational table),也会有根据hibernate.cfg.xml的配置创建表,或是删除创建表。顺序也应该是这样的,原因很简单,POJO翻译成关系表的过程跟数据库有关;最后面是表操作,肯定就需要数据库连接以及数据库表信息。

是这样吧,应该没啥问题对吧。这个猜想还是比较符合正常人的思维的,事实上Hibernate的cfg.buildSessionFactory()也是这么做的,只不过,它还其它一些操作。比如,缓存。

至于为什么说buildSessionFactory为什么就会有绑定数据库连接,这个其实非常简单。SessionFactory就相当于数据库连接池,或者说它就是加强版的数据库连接池,即是说它提供带缓存、抓取方案等加强功能的数据库连接池。是池中霸主了。
曾经,我们是这么做
DriverManager.getConnection(user,pwd);
后来是这样的:
dataSource.getConnection();
再后来是这样的:
dbcp.getConnection(); (dbcp : 数据库连接池对象)
现在成了这样的:
sessionFactory.openSession(); (相当获得Connection,其实Connection只是Session中一部分,但一个Connection必须绑定Session对象)

Hibernate有内置的(build-in)连接池,也可以用第三方连接池,如:Proxool、C3P0。请参考hibernate.properties.template。

在这里,我顺便提出一个疑问,即hibernate.cfg.xml中既有Connection.driver_class,同时也有dialect。它是允许有一项缺省的,但不能有一项是错。请大师指点。


到这里,我们还没开始读代码,一直说着废话,做着准备动作。

综上所述,你一定知道,我们应该关注哪个方法。对于confugure而言只是做配置文件的解析。因此,重点自然就在buildSessionFactory上面了。当然,大家也可以读读configure部分代码。由于它的配置文件很简单,不像Struts2那般复杂,所以它的代码比较简单,没有很巧代码。但不防看看大师们是怎么做。我敢肯定,你以后也会写类似的功能,所以你值得一看。


buildSessionFactory,都说这个操作很耗资源,它怎么就耗资源了呢?

下面来看看Configuration#buildSessionFactory()庐山真面目(Hibernate4.0开始,把buildSessionFactory()注解为Deprecated,但应该没有废弃的。首先,构建ServiceRegistry对象非常困难;其次,暂时一般不需要;最后,现在ServiceRegistry挺好,暂时不需要扩展。)因此,buildSessionFactory方法构建了ServiceRegistry,然后开始Configuration#buildSessionFactory( ServiceRegistry serviceRegistry)方法上。

SessionFactory创建可以说是历尽千辛万苦,主要在两个地方,大家可以自行查看其源码的代码量。1. Configuration#secondPassComplie(),这方法是将对象翻译成关系表的主要方法,处理对象之间的关联关系并对应成数据库表之间的关联关系。2.new SessionFactoryImpl(),它关系到整个SessionFactory初始化,做的事也相当宏伟。


public SessionFactory buildSessionFactory(ServiceRegistry serviceRegistry) throws HibernateException {LOG.debugf( "Preparing to build session factory with filters : %s", filterDefinitions );buildTypeRegistrations( serviceRegistry );secondPassCompile();  // 这货很重要,它是对象翻译成关系表。反正这句话的工作强度很大。if ( !metadataSourceQueue.isEmpty() ) {LOG.incompleteMappingMetadataCacheProcessing();}// 检查的是 Annotation validate();Environment.verifyProperties( properties );Properties copy = new Properties();copy.putAll( properties );ConfigurationHelper.resolvePlaceHolders( copy );Settings settings = buildSettings( copy, serviceRegistry );// 创建一个 SessionFactory的实例。这里面同样包罗万象return new SessionFactoryImpl(this,mapping,serviceRegistry,settings,sessionFactoryObserver);}
new SessionFactoryImpl()这地方的代码,也是比较朴实,就长,很长。整个Hibernate的源码都中规中矩,都比较好理解,就是比较长。


其中,有一个地方很不好理解: ServiceRegistry#getService(Class<?> clz) 。
这个地方,我觉得是很不好理解的,我个人是这么看它,也不知道对与不对。
曾经有一种设计模式叫对象池(ObjectPool),它能帮我们管理对象。它有操作是这样的:
T obj = pool.getObject(Class<T> clz);
可以拿Class<T>的实例。
后来,有一个框架叫Struts2。它呢,有个东西(Container)也能帮我们管理对象,还能管理对象生命周期,它有个操作是这样的:
T obj = container.getInstance(Class<T> clz);
也可以拿Class<T>的实例。
现在,HIbernate有个货,叫ServiceRegistry,它也能帮我们管理对象,还能更具体管理对象生命周期,之外还有分层。它也有一个操作像是这样的:
T obj = serviceRegistry.getService(Class<T> clz);
同样也可以拿到一个Class<T>的实例。

我不会告诉你,Spring也有这样的东西的。我也不会告诉你它能松藕的,我会告诉你它还给方便了单元测试吗?

关于ServiceRegistry,这只讲这么多。你可以把它看成一个对象池,这种方式给阅读代码的我们带来很多苦恼、强加很多阻力、加大难度。因此,我们必须会写类似的代码,让别人也苦恼、也有难度。后面还会继续讲。


另有一点,需要关注的,那就是在构建SessionFactory,也不单是构建SessionFactory,就是一类含有框架初始化信息的,如配置信息,都会是这样的,包括Struts2。对于这一类属性必须是恒定的、不可变的。因此,我们看到像:
this.classMetadata = Collections.unmodifiableMap(classMeta); 
和 
final PersistentClass model = (PersistentClass) classes.next();
对一般类型可以用final修饰,但对Collection来说,就需要用Collections.unmodifiableXXX(Collection)的方式将资源标记为不可变。

Hibernate在处理配置信息,初始化系统的时候并没有用到Builder模式。这主要是因为Hibernate初始化资源的时候,信息比较集中,且稳定。因此直接可以返回创建的实例。

今天先到这里吧,废话说了挺多的。
下面几个类,是可以关注的。


org.hibernate.mapping.PersistentClass : Mapping for an entity
org.hibernate.mapping.Table : A relational table
org.hibernate.cfg.annotations.EntityBinder :  Stateful holder and processor for binding Entity information


(SessionFactoryImpl & Configuration也可以了解一下。)
原创粉丝点击