【问题标题】:OSGi Fragment bundle with Hibernate having Runtime pojos具有运行时 pojos 的带有 Hibernate 的 OSGi 片段包
【发布时间】:2012-09-10 09:50:08
【问题描述】:

我的要求是使用 hibernate 映射各种数据库(特别是 SQL Server、MySQl 和 Postgres);从 db 记录创建一个 xml 文件。

对于休眠,我正在使用 JAssist 在运行时创建 hbm 文件和 pojos。 我的代码运行良好,为了进一步模块化,我为每个数据库实现了片段包。这样我的主机包将处理运行时类的创建并将它们添加到类加载器、hbm 文件创建逻辑和 BL 中。片段通过传递参数调用它。

当我为每个数据库创建一个片段包时, 在我的主机包中创建的运行时 pojo 类在我的片段包中可见, 我检查了“Thread.currentThread().getContextClassLoader().loadClass()” 并能够创建它的实例,

问题是 当我从片段包调用 Hibernate 函数时,我得到“实体未映射”,AFAIK 当休眠无法找到带有表的映射类时出现这些异常。 所以我猜 Hibernate 没有找到我的运行时 pojo 类。它可以在主机中找到。

主机: 运行时 Pojo 创建, HBM 和 CFG 创建和更新逻辑 BL

片段: 休眠层, 调用休眠函数, XML 创建逻辑

【问题讨论】:

    标签: java hibernate osgi


    【解决方案1】:

    Hibernate OSGi 目前有几个注意事项,其中一个需要单个持久性单元客户端捆绑包。由于各种原因,我们在构建负责处理持久性实体、映射和资源的 ClassLoader 时必须使用“requestingBundle”。

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

    OsgiPersistenceProviderService 和 OsgiSessionFactoryService 都在调用服务时将“requestingBundle”添加到 ClassLoader。正如 Johanna 建议的那样,除了 requestingBundle 或 persistence.xml 文件本身的位置之外,没有真正好的方法来了解哪些 Bundle 构成了持久性单元。

    不过,我很想听听有关如何更好地支持这样的设置的想法。我最初在清单中使用了额外的元数据来表示“我是持久性单元 x 的一部分”,但从未真正有时间仔细考虑。

    绝对使用上面推荐的解决方案,使用 ClassLoaderHelper。这是一个完全临时的 hack,将在 ORM 5 中消失。它的存在纯粹是由于 ORM 4 的静态特性。

    【讨论】:

    • 感谢 brmeyer 提供的宝贵信息,但我们已迁移到 jdbc。
    【解决方案2】:

    看看 org.hibernate.internal.util.ClassLoaderHelper。

    您所要做的就是将 ClassLoader 替换为能够解析实体类的 ClassLoader。 Hibernate-Osgi 也将其设置为 OSGI ClassLoader(参见 org.hibernate.osgi.HibernateBundleActivator)。所以建议如下:

    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();
    }
    

    我通过覆盖执行此例程并返回 super.buildSessionFactory 的 buildSessionFactory 将其放入我的 HibernateConfiguration 类。

    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;
    }
    
    }
    

    最后是 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;
    }
    
    }
    

    我建议在每个 BundleActivators 中创建一个新的 BundleClassLoader 并将其添加到某种注册表中,以便 BundleWideClassLoader 可以从那里获取 BundleClassLoader 列表。当 bundle 停止或删除时,不要忘记删除 BundleClassLoader。

    【讨论】:

      【解决方案3】:

      如果您在多个捆绑包上使用 Hibernate,则始终会出现此问题。在 Hibernate 配置中,您无法确定在哪个 Bundle 中可以找到映射文件和 pojo 类文件。 Hibernate 不使用 OSGI 为此提供的机制。因此,hibernate 只能找到与 Hibernate 库位于同一捆绑包中的映射文件和类。

      我不知道是否有针对此问题的专业解决方案(第三方产品)。

      解决这个问题有两种可能:

      1. 忘记您的片段包,将所有 Hibernate 库、映射文件、pojo、使用 Hibernate/HQL 的所有数据库的类放在一个包中。使用不同的hibernate.cfg.xml文件时可以在不同的数据库之间切换;每个数据库都有自己的配置文件。这些 hibernate.cfg.xml 文件可以在包之外。

      2. 编写你自己的扩展 org.hibernate.cfg.Configuration 的配置类,在这个类中你必须

        • 编写自己的类加载器,即使在其他包中也能找到 pojo 类
        • 覆盖 addResource(String resourceName, ClassLoader classLoader),使其在其他包中也能找到资源
        • 覆盖 doConfigure 和 buildSessionFactory 以便它们使用您的类加载器而不是标准类加载器(使用 Thread.setContextClassLoader 并从超类调用方法,即从标准 Hibernate 配置类)。
        • 覆盖所有其他返回 Configuration 实例的方法,以便它们返回 Configuration 类的实例,而不是 Hibernate Configuration 类的实例。

      我们做了解决方案 2。这有点工作,但现在运行良好。 (想一想,当再次更改 Hibernate 版本时,可能需要做一些工作。)

      【讨论】:

        猜你喜欢
        • 2017-12-14
        • 2023-03-06
        • 1970-01-01
        • 2016-04-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-26
        相关资源
        最近更新 更多