在OSGI(felix)里整合hibernate-阿里云开发者社区

开发者社区> anrainie> 正文

在OSGI(felix)里整合hibernate

简介: 早前的时候写了一篇《在插件里应用hsqldb和hibernate》 但是之前的方法只适用于equinox环境,并不能算是hibernate和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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
使用NAT网关轻松为单台云服务器设置多个公网IP
在应用中,有时会遇到用户询问如何使单台云服务器具备多个公网IP的问题。 具体如何操作呢,有了NAT网关这个也不是难题。
25934 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
7612 0
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
9948 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
9323 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
11441 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
10944 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
6404 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
2988 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
6196 0
+关注
anrainie
Eclipse RCP开发者。
113
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载