zoukankan      html  css  js  c++  java
  • 在OSGI(felix)里整合hibernate

    早前的时候写了一篇《在插件里应用hsqldb和hibernate》

    但是之前的方法只适用于equinox环境,并不能算是hibernate和osgi的完整整合,昨天终于完成了felix+hibernate的整合方式,现做一个完整记录。源码地址如下:

    SVN:http://ext-eclipse.googlecode.com/svn

    位置在trunck/mos下,主要是三个maven project: mos.hibernate,mos.hibernate.extender,mos.hibernate.manager

    还有一个demo: mos.hibernate.demo,该插件并不完善,只提供了基本的使用方式,启动它需要blueprint配置。

    先说明下三个主要插件的功能以及实现的思路:

    mos.hibernate:

    提供hibernate所需要的全部jar包,以后如果有版本变更,全部在这里维护。该插件不依赖于任何业务包。

    清单包括(此处默认dom4j已经作为独立bundle,不计入其中):

    <Bundle-ClassPath>.,lib/ejb3-persistence.jar,lib/hibernate-annotations.jar,lib/hibernate-commons-annotations.jar,lib/hibernate-entitymanager.jar,lib/antlr-2.7.6.jar,lib/asm-all-3.3.1.jar,lib/c3p0-0.9.1.jar,lib/cglib-2.2.2.jar,lib/commons-collections-2.1.1.jar,
    lib/commons-logging-1.0.4.jar,lib/concurrent-1.3.4.jar,lib/ehcache-1.2.3.jar,lib/hibernate-3.2.7.ga.jar,lib/jta-1.1.jar</Bundle-ClassPath>

    osgi整合hibernate最大的问题在于bundle的ClassLoader是分离的,而hibernate大量使用了反射方法,且在它设计本身,是没有考虑到多ClassLoader的情况的。

    在绝大多数代码里,它使用了Thread.currentThread().getContextClassLoader()这个ClassLoader,于是只能获取到当前bundle能访问到的类。

    如果是equinox环境,要解决这个问题就相对简单,eclipse为我们提供了一个buddy策略,参见《RCP Buddy简易实现》

    但是,这对于osgi是破坏性的,它完全忽略了osgi规范定下的访问级别。

    在felix环境下,我们有另外的替代方案:

    方案一:

    1、fragment bundle

    我们可以把数据库驱动放在一个一个的fragment bundle中,让它的main bundle设为mos.hibernate,这样就实现了hibernate没有依赖于业务插件,也可以相对灵活的配置数据库

    但是明显的,它有局限性。

    2、DynamicImport-Package

    这是OSGI规范内部的东西,它的作用是动态的查找可导入的包。

    示例:

    <DynamicImport-Package>*</DynamicImport-Package>

    既然它符合osgi规范,那么,就必然无法动态导入已经依赖了mos.hibernate插件的其他插件中的package。

    对于数据库驱动来说,这不是个问题。

    但是对于业务Entity插件来说,这是必然会矛盾的地方。

    所以对于业务Entity插件,我们还有另外的处理方式,将在mos.hibernate.manager讲解处说明。

    mos.hibernate.extender:

    1、注册数据库配置(HbmDatabaseConfig)和映射配置(HbmMappingConfig)

    2、监听bundle的启动和停止,并解析其MANIFEST.MF文件,获取<Database-Configuration>,<Mapping-Configuration>的配置信息。

    配置格式如下:

    <Database-Configuration>[sessionFactoryId];[hibernate.cfg.xml File Path],[sessionFactoryId];[hibernate.cfg.xml File Path]</Database-Configuration>

    <Mapping-Configuration>[sessionFactoryId];[Entity class name],[sessionFactoryId];[Entity class name]</Mapping-Configuration>

    示例:

    <Database-Configuration>hsqldb;/config/hsqldb.cfg.xml</Database-Configuration>
    <Mapping-Configuration>hsqldb;mos.hibernate.demo.DemoBean,hsqldb;mos.hibernate.demo.Cat</Mapping-Configuration>

    该bundle的实现有点类似于eclipse的扩展点机制。

    事实上extender是一种非常值得学习的插件编程方式。

    它由两个部分组成:

    1)通过 context.getBundles()遍历全部已经存在的bundle

    2)使用BundleListener,来对BundleEvent.STARTED状态的bundle注册,和BundleEvent.STOPPING状态的bundle反注册

    同时,它提供对MANIFEST.MF文件的解析

    bundle.getHeaders()能获取全部的MF属性名。

    参见mos.hibernate.extender.util.HibernateBundleHelper的实现

     

    mos.hibernate.manager:

    1、提供数据库配置(HbmDatabaseConfig)和映射配置(HbmMappingConfig)的定义

    2、提供数据库配置容器的定义以及缓存。

    3、提供外部访问接口DAO(暂未完善)

    这个插件的实现难点主要在于,使hibernate能获取到业务实体的Class实例。

    通常的Configuration类的实现里,hibernate是对org.dom4j.Document的解析,获取类名,然后通过反射来实现获取实例的。

    在osgi环境下,这种方式遇到了困难。

    我们来使用另外几个jar包:

    1、hibernate-annotations.jar

    2、ejb3-persistence.jar

    hibernate-annotations.jar提供一个AnnotationConfiguration

    它继承于Configuration,但是提供了特别的对Class的处理。

    如下:

    @Override
        protected void parseMappingElement(Element subelement, String name) {
            Attribute rsrc = subelement.attribute( "resource" );
            Attribute file = subelement.attribute( "file" );
            Attribute jar = subelement.attribute( "jar" );
            Attribute pckg = subelement.attribute( "package" );
            Attribute clazz = subelement.attribute( "class" );
            if ( rsrc != null ) {
                log.debug( name + "<-" + rsrc );
                addResource( rsrc.getValue() );
            }
            else if ( jar != null ) {
                log.debug( name + "<-" + jar );
                addJar( new File( jar.getValue() ) );
            }
            else if ( file != null ) {
                log.debug( name + "<-" + file );
                addFile( file.getValue() );
            }
            else if ( pckg != null ) {
                log.debug( name + "<-" + pckg );
                addPackage( pckg.getValue() );
            }
            else if ( clazz != null ) {
                log.debug( name + "<-" + clazz );
                Class loadedClass = null;
                try {
                    loadedClass = ReflectHelper.classForName( clazz.getValue() );
                }
                catch (ClassNotFoundException cnf) {
                    throw new MappingException(
                            "Unable to load class declared as <mapping class=\"" + clazz.getValue() + "\"/> in the configuration:",
                            cnf
                    );
                }
                catch (NoClassDefFoundError ncdf) {
                    throw new MappingException(
                            "Unable to load class declared as <mapping class=\"" + clazz.getValue() + "\"/> in the configuration:",
                            ncdf
                    );
                }
    
                addAnnotatedClass( loadedClass );
            }
            else {
                throw new MappingException( "<mapping> element in configuration specifies no attributes" );
            }
        }

    注意addAnnotatedClass这个方法:

    它的实现是:

    public AnnotationConfiguration addAnnotatedClass(Class persistentClass) throws MappingException {
            XClass persistentXClass = reflectionManager.toXClass( persistentClass );
            try {
                annotatedClasses.add( persistentXClass );
                return this;
            }
            catch (MappingException me) {
                log.error( "Could not compile the mapping annotations", me );
                throw me;
            }
        }

    于是我们可以猜想,被注册的Class已经维护在了annotatedClasses里。

    事实上也是如此。

    一个简单的Annotated实体类的定义如下:

    package mos.hibernate.demo;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import javax.persistence.TableGenerator;
    
    /**
     * @author caiyu
     * @date 2012-10-11 下午2:41:21
     */
    @Entity
    @Table(name = "CAT")
    public class Cat {
        @Id
        @TableGenerator(name = "CAT", allocationSize = 1)
        @GeneratedValue(strategy = GenerationType.TABLE, generator = "CAT")
        private int id;
        
        @Column(name="name")
        private String name;
        
        @Column(name="color")
        private String color;
    
        public Cat() {
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("ID:");
            sb.append(id);
            sb.append("\n");
            sb.append("Name:");
            sb.append(name);
            sb.append("\n");
            sb.append("Color:");
            sb.append(color);
            sb.append("\n");
            return sb.toString();
        }
    }

    它不再需要另一个单独的XML文件来定义它的hibernate映射关系,这些映射关系已经被它本身的annotation定义描述出来了。

    要注意的细节问题:

    1、dom4j找不到驱动和NoClassDefFoundError问题,这是因为dom4j需要的xerces和xml-apis已经被jdk 1.4以及以后版本引入了,在config.properties配置里添加:

    # The following property makes specified packages from the class path
    # available to all bundles. You should avoid using this property.

    org.osgi.framework.bootdelegation=javax.*,org.*

    即是使jre下的javax.*和org.*对osgi的全部bundle可见。

    当然也可以将xerces和xml-apis的合适版本插件化,但这样比较麻烦,且可能会导致类名重复的问题。

    2、使用该整合的bundle,需要import如下package:

    mos.hibernate.manager,org.hibernate,javax.persistence,org.hibernate.proxy,net.sf.cglib.proxy

  • 相关阅读:
    htnl5中设置文本单行显示,超出部分打省略号,鼠标移到文本时alt出全部文本内容
    sql 查出一张表中重复的所有记录数据
    JS实现关闭当前子窗口,刷新父窗口
    jstl <c:forEach> 介绍
    Oracle SQL: TO_CHAR and TO_NUMBER 笔记
    Python学习记录七---继承、多态和封装
    iOS动画和第三方插件学习网址
    Python学习记录(六)--函数 定义和使用
    python学习记录(五) --语句块和比较符
    Python学习记录(四)--字典
  • 原文地址:https://www.cnblogs.com/anrainie/p/2725722.html
Copyright © 2011-2022 走看看