简体   繁体   English

OSGi Fragment包含Hibernate,具有Runtime pojos

[英]OSGi Fragment bundle with Hibernate having Runtime pojos

My requirement is to map various database (particularly SQL Server, MySQl and Postgres) using hibernate ; 我的要求是使用hibernate映射各种数据库(特别是SQL Server,MySQl和Postgres); from db record create a xml file. 从db record创建一个xml文件。

For hibernate i am creating hbm files and pojos at run-time using JAssist. 对于hibernate,我使用JAssist在运行时创建hbm文件和pojos。 My code works great, for further modularization i implement fragment bundles for each database.so that my host bundle will handle runtime class creation and adding them in classloader, hbm file creation logic, and BL. 我的代码工作得很好,为了进一步模块化,我实现了每个数据库的片段包。因此我的主机包将处理运行时类创建并将它们添加到类加载器,hbm文件创建逻辑和BL中。 fragment calls it by passing parameters. 片段通过传递参数来调用它。

when i create a fragment bundle for each database, the runtime pojo class created in my host bundle is visible in my fragment bundle, i checked with "Thread.currentThread().getContextClassLoader().loadClass()" and able to create its instance, 当我为每个数据库创建一个片段包时,我的宿主包中创建的运行时pojo类在我的片段包中可见,我使用“Thread.currentThread()。getContextClassLoader()。loadClass()”检查并能够创建它的实例,

The Problem is when i call Hibernate functions from fragment bundle, I am getting "Entity not mapped", AFAIK These exception comes when hibernate is unable to find the mapping class with table. 问题是当我从片段bundle调用Hibernate函数时,我得到“实体未映射”,AFAIK当hibernate无法找到带有表的映射类时,会出现这些异常。 So i guess Hibernate is not finding my runtime pojo classes. 所以我猜Hibernate没有找到我的运行时pojo类。 which it can find in host. 它可以在主机中找到。

Host : Runtime Pojo creation, HBM and CFG creation and updation logic BL 主机:运行时Pojo创建,HBM和CFG创建和更新逻辑BL

Fragment : Hibernate layer, Calling Hibernate function, XML Creation logic 片段:Hibernate层,调用Hibernate函数,XML创建逻辑

This problem always appears if you use Hibernate over more than one bundle. 如果您在多个捆绑包上使用Hibernate,则始终会出现此问题。 In the Hibernate configuration you can't tell in which Bundle the mapping files and the pojo class files can be found. 在Hibernate配置中,您无法判断可以在哪个Bundle中找到映射文件和pojo类文件。 Hibernate does not use the mechanisms which OSGI provides for this. Hibernate不使用OSGI为此提供的机制。 As a result hibernate only finds mapping files and classes which are in the same bundle as the Hibernate library. 因此,hibernate只能找到与Hibernate库位于同一个bundle中的映射文件和类。

I don't know if there is anywhere a professional solution (a third party product) for this problem. 我不知道是否有任何针对此问题的专业解决方案(第三方产品)。

There are two possibilities to solve this problem: 解决此问题有两种可能性:

  1. Forget your fragment bundles and put all Hibernate libraries, mapping files, pojos, classes using Hibernate/HQL for all databases into a single bundle. 忘记你的片段包,并将所有Hibernate库,映射文件,pojos,使用Hibernate / HQL的类放到一个包中。 You can switch between different databases when you use different hibernate.cfg.xml files; 使用不同的hibernate.cfg.xml文件时,可以在不同的数据库之间切换; each database has its own configuration file. 每个数据库都有自己的配置文件。 These hibernate.cfg.xml files can be outside the bundles. 这些hibernate.cfg.xml文件可以在bundle之外。

  2. Write your own Configuration class which extends org.hibernate.cfg.Configuration, In this class you have to 编写自己的扩展org.hibernate.cfg.Configuration的Configuration类,在这个类中你必须这样做

    • write your own class loader which finds the pojo classes even in other bundles 编写自己的类加载器,即使在其他bundle中也能找到pojo类
    • override addResource(String resourceName, ClassLoader classLoader) in a way it finds resources also in other bundles 覆盖addResource(String resourceName,ClassLoader classLoader),它也可以在其他包中找到资源
    • override doConfigure and buildSessionFactory so they use your class loader instead of the standard class loader (use Thread.setContextClassLoader and call the method from the super class, ie from the standard Hibernate Configuration class). 覆盖doConfigure和buildSessionFactory,以便它们使用类加载器而不是标准类加载器(使用Thread.setContextClassLoader并从超类调用方法,即从标准的Hibernate Configuration类调用)。
    • override all the other methods which return an instance of Configuration so they return the instance of your Configuration class and not of the Hibernate Configuration class. 覆盖返回Configuration实例的所有其他方法,以便它们返回Configuration类的实例,而不是Hibernate Configuration类的实例。

We did solution 2. It was a bit of work, but now it is running well. 我们做了解决方案2.这是一项工作,但现在运行良好。 (Thought, when changing the Hibernate version again a bit of work might be necessary.) (想想,当再次更改Hibernate版本时,可能需要做一些工作。)

have a look on org.hibernate.internal.util.ClassLoaderHelper. 看看org.hibernate.internal.util.ClassLoaderHelper。

All you have to do, is replacing the ClassLoader with a ClassLoader which is able to resolve your entity classes. 您所要做的就是用ClassLoader替换ClassLoader,ClassLoader能够解析您的实体类。 Hibernate-Osgi is also setting it to the OSGI ClassLoader (see org.hibernate.osgi.HibernateBundleActivator). Hibernate-Osgi也将它设置为OSGI ClassLoader(参见org.hibernate.osgi.HibernateBundleActivator)。 So suggest the following: 建议如下:

BundleWideClassLoader cl = new BundleWideClassLoader();

if (ClassLoaderHelper.overridenClassLoader != null 
    && ClassLoaderHelper.overridenClassLoader instanceof OsgiClassLoader) 
{
    OsgiClassLoader ocl = (OsgiClassLoader)ClassLoaderHelper.overridenClassLoader;
    for (Bundle b : cl.getBundles()) {
        ocl.addBundle(b);
    }
} else {
    ClassLoaderHelper.overridenClassLoader = new BundleWideClassLoader();
}

I put this to my HibernateConfiguration class by overriding buildSessionFactory which performs this routine and returns super.buildSessionFactory. 我通过重写buildSessionFactory将它放到我的HibernateConfiguration类中,它执行此例程并返回super.buildSessionFactory。

The BundleWideClassLoader looks like this BundleWideClassLoader看起来像这样

public class BundleWideClassLoader extends ClassLoader
{

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    for (BundleClassLoader cl : this.getAllClassLoader()) {
        try {
            Class clazz = cl.findClass(name);
            return clazz;
        } catch (Exception ex) {
        }
    }
    throw new ClassNotFoundException(name);
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    Class clazz = this.findClass(name);
    if (resolve) {
        this.resolveClass(clazz);
    }
    return clazz;
}

@Override
public URL findResource(String name) {
    for (BundleClassLoader cl : this.getAllClassLoader()) {
        URL ret = cl.findResource(name);
        if (ret != null) {
            return ret;
        }
    }
    return null;
}

/**
 * Returns a list of all available BundleClassLoader.
 *
 * @return classloader
 */
public HashSet<BundleClassLoader> getAllClassLoader() {
    //
    // Do some magic here to get your ClassLoaders from all of your Bundles
    //
}

/**
 * Returns a list of all bundles which are registered in this BundleWideClassLoader.
 *
 * @return list of managed bundles
 */
public HashSet<Bundle> getBundles() {
    HashSet<Bundle> bundles = new HashSet<>();
    for (BundleClassLoader cl : this.getAllClassLoader()) {
        bundles.add(cl.getBundleContext().getBundle());
    }
    return bundles;
}

}

And finally the BundleClassLoader: 最后是BundleClassLoader:

public class BundleClassLoader extends ClassLoader
{
/**
 * Bundle context.
 */
private BundleContext context;

/**
 * Constructor.
 * @param ctx Bundle Context
 */
public BundleClassLoader(BundleContext ctx) {
    this.context = ctx;
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    return this.context.getBundle().loadClass(name);
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    Class clazz = this.findClass(name);
    if (resolve) {
        this.resolveClass(clazz);
    }

    return clazz;
}

@Override
public URL findResource(String name) {
    return this.context.getBundle().getResource(name);
}

/**
 * Returns bundle context.
 * @return bundle context
 */
public BundleContext getBundleContext() {
    return this.context;
}

}

I suggest to create a new BundleClassLoader in each of your BundleActivators and add it to some kind of registry so the BundleWideClassLoader can get a list of BundleClassLoader from there. 我建议在每个BundleActivators中创建一个新的BundleClassLoader并将其添加到某种注册表中,以便BundleWideClassLoader可以从那里获得BundleClassLoader列表。 Do not forget to remove the BundleClassLoader, when the bundle is stopped or removed. 当捆绑被停止或移除时,不要忘记删除BundleClassLoader。

Hibernate OSGi currently has several caveats, one of which requires a single persistence unit client bundle. Hibernate OSGi目前有几个注意事项,其中一个需要一个持久性单元客户端包。 For a variety of reasons, we have to use the "requestingBundle" when we build up the ClassLoader responsible for handling the persistence entities, mappings, and resources. 出于各种原因,我们在构建负责处理持久性实体,映射和资源的ClassLoader时必须使用“requesBundle”。

Have a look at: https://github.com/hibernate/hibernate-orm/blob/master/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiClassLoader.java 看看: https//github.com/hibernate/hibernate-orm/blob/master/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiClassLoader.java

OsgiPersistenceProviderService and OsgiSessionFactoryService both add the "requestingBundle" to the ClassLoader when the service is called. OsgiPersistenceProviderService和OsgiSessionFactoryService都在调用服务时将“requesBundle”添加到ClassLoader。 As Johanna suggested, there's not really a decent way to know which Bundles make up the persistence unit, other than requestingBundle or the location of the persistence.xml file itself. 正如Johanna建议的那样,除了requestedBundle或persistence.xml文件本身的位置之外,还没有一种方法可以知道哪些Bundles构成了持久性单元。

However, I'd love to hear ideas on how to better support a setup like this. 但是,我很想听听如何更好地支持这样的设置的想法。 I had originally played around with additional metadata in the manifests to signify "I'm a part of persistence unit x", but never really had time to think it through. 我最初在清单中使用了额外的元数据来表示“我是持久性单元x的一部分”,但从来没有真正有时间思考它。

Definitely do not use the solution recommended above using ClassLoaderHelper. 绝对使用上面使用ClassLoaderHelper建议的解决方案。 That is a completely temporary hack that will be gone in ORM 5. It's there purely due to the static nature of ORM 4. 这是一个完全暂时的破解,将在ORM 5中消失。这纯粹是因为ORM 4的静态特性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM