3

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

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

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

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

Host : 运行时 Pojo 创建、HBM 和 CFG 创建和更新逻辑 BL

Fragment : Hibernate 层,调用 Hibernate 函数,XML 创建逻辑

4

3 回答 3

3

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

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

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

  1. 忘记您的片段包,将所有使用 Hibernate/HQL 的 Hibernate 库、映射文件、pojo、类用于所有数据库放入一个包中。使用不同的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 版本时,可能需要做一些工作。)

于 2012-09-18T16:09:55.163 回答
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 的静态特性。

于 2013-10-29T22:37:48.520 回答
1

看看 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 列表。当捆绑停止或删除时,不要忘记删除 BundleClassLoader。

于 2013-10-29T10:47:31.610 回答