2009年2月26日星期四

Hibernate 的Configuration和它的ClassLoader.

还是在整理遗留系统的hibernate问题。
现状:
1)应用是eclipse RCP应用。多个自定义的plugin。其中一个是用于共享通用代码目的的base plugin。
2)Hibernte2.0版。在base plugin项目中包含了一个hibernte.cfg.xml,在其lib目录下有hibernate2.jar。其它plugin的lib目录也有hibnerate2.jar,也在使用自己的hibernate.cfg.xml.

问题:
1)开始试图让整个应用只有一个sessionFactory实例。发现这就有个项目依赖问题。如果把hibernate sessionFactory的管理放在base-plugin项目里。那么其它项目如果需要自己的hibernate dao 访问怎么办?虽然可以通过在base-plugin模块里定义一些配置文件读取点的方式加载额外的hibernate mapping文件,但是,如何把子项目的class加到base-plugin的classpath中呢?(也许可以写自己的classloader)。通过配置文件是解决依赖的一个好办法。但是,没有写classloader的经验,也没有时间去做这种尝试。这种逻辑放在框架里比较合适,而不是放在仅仅作为工具库的base project里。

2)接下来就考虑把Hibernate的jar文件只放在base-plugin的lib中。如果那个项目需要书库访问功能,就在自己的(子)项目中写new Configuration().configure().buildSessionFactory()。觉得这还算个差不多干净的办法。当进行代码实现时,技术性问题又冒出来了。以下蓝色文字仅代表自己对所遇问题的一些理解和猜测。plugin运行时有自己的classloader。而hibernte的Congfiguration().configure()在搜索hibernate.cfg.xml时是从加载Configuration这个类的classloader的classpath路径中寻找。这在hibernate2.jar仅存在于base项目的情况下是找不到你子项目的hibernate配置的,它又重复加载了base项目中的hibernate配置。查看Hibernat API文档,知道可以给configure()传入resource参数指定hibernate.cfg.xml的位置。通过AbcClass.class.getClassLoader().getResource("/abcHibernate.cfg.xml");的方式指定了abc项目用的.cfg.xml文件。Ok. Hibernate 被成功初始化了。但是,当使用hibernate query后,访问list的元素时发生了ClassCaseException,跟踪的SDK的source, List还是跟classloader有关系的。

3)接下来的想法就是把hibernate的jar文件放在各自project的lib里。应用程序在启动时,启动一个线程初始化sesssionFactory(),问题又来了。当第二个sessionFactory在同一个线程中初始化时,得到了另一个运行时 exception: Exception in thread "Thread-2" java.lang.LinkageError: Class a/b/c/d/Abc.class violates loader constraints. ... ... google到这段。没办法了。就先初始化一个sessionFactory吧。只是不知道Hibernte3.0是否也是同样的机制。
Hibernate always uses the classloader of the current running thread, which guarantees predictable behavior in all environments. In some environments with weak classloader/deployment configuration options (SAP, JONAS, etc.), it is necessary to tell Hibernate to use a different classloader, not the loader that loaded hibernate2.jar. Usually, this should be transparent to Hibernate and the application server would know what classloaders to use, depending on the packaging. Also note that this is not an issue if the "one application - one Hibernate version" strategy is used, which is the case in most production environments (you have dedicated libraries per application).

We currently allow a customizable classloader for addResource(), but this only loads the mappings with the given classloader. We would need an option to specify a classloader for the persistent classes as well, basically, for all Hibernate operations.
http://opensource.atlassian.com/projects/hibernate/browse/HB-1310
经验:
是不是common的功能,有时候不能仅仅从功能上划分,技术细节也是重要的。

====下面是程序运行时打印的log, 可以看出currentThread的classloader都是null(
Some implementations may use null to represent the bootstrap class loader.)。而不同plugin的classloader是不同的。
Current Thread's classLoader: null
BtafHibernateUtil's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1d520c4
Configuration's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1d520c4
cf's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1d520c4


Current Thread's classLoader: null
SessionFactory's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1c6f579
Configuration's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1c6f579
cf's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1c6f579