3

私の要件は、hibernateを使用してさまざまなデータベース(特にSQL Server、MySQl、Postgres)をマップすることです。dbレコードからxmlファイルを作成します。

hibernateの場合、JAssistを使用して実行時にhbmファイルとpojoを作成しています。私のコードはうまく機能します。さらにモジュール化するために、データベースごとにフラグメントバンドルを実装します。これにより、ホストバンドルがランタイムクラスの作成を処理し、クラスローダー、hbmファイル作成ロジック、およびBLに追加します。フラグメントは、パラメーターを渡すことによってそれを呼び出します。

データベースごとにフラグメントバンドルを作成すると、ホストバンドルで作成されたランタイムpojoクラスがフラグメントバンドルに表示されます。「Thread.currentThread()。getContextClassLoader()。loadClass()」で確認すると、そのインスタンスを作成できます。 、

問題は、フラグメントバンドルからHibernate関数を呼び出すと、「エンティティがマップされていません」というメッセージが表示されることです。これらの例外は、hibernateがテーブルでマッピングクラスを見つけられない場合に発生します。したがって、Hibernateはランタイムpojoクラスを見つけていないと思います。それはホストで見つけることができます。

ホスト:ランタイムPojoの作成、HBMおよびCFGの作成、および更新ロジックBL

フラグメント:Hibernateレイヤー、Hibernate関数の呼び出し、XML作成ロジック

4

3 に答える 3

3

この問題は、複数のバンドルでHibernateを使用する場合に常に発生します。Hibernate構成では、マッピングファイルとpojoクラスファイルがどのバンドルにあるかを判断できません。Hibernateは、OSGIがこれに提供するメカニズムを使用しません。その結果、hibernateはHibernateライブラリと同じバンドルにあるマッピングファイルとクラスのみを検索します。

この問題の専門的な解決策(サードパーティ製品)がどこかにあるかどうかはわかりません。

この問題を解決するには、次の2つの可能性があります。

  1. フラグメントバンドルを忘れて、すべてのデータベースにHibernate / HQLを使用するすべてのHibernateライブラリ、マッピングファイル、pojo、クラスを1つのバンドルにまとめます。異なるhibernate.cfg.xmlファイルを使用する場合は、異なるデータベースを切り替えることができます。各データベースには独自の構成ファイルがあります。これらのhibernate.cfg.xmlファイルはバンドルの外部にある可能性があります。

  2. org.hibernate.cfg.Configurationを拡張する独自のConfigurationクラスを作成します。このクラスでは、次のことを行う必要があります。

    • 他のバンドルでもpojoクラスを見つける独自のクラスローダーを作成する
    • addResource(String resourceName、ClassLoader classLoader)をオーバーライドして、他のバンドルでもリソースを検索できるようにします
    • doConfigureとbuildSessionFactoryをオーバーライドして、標準のクラスローダーの代わりにクラスローダーを使用するようにします(Thread.setContextClassLoaderを使用して、スーパークラス、つまり標準のHibernate Configurationクラスからメソッドを呼び出します)。
    • Configurationのインスタンスを返す他のすべてのメソッドをオーバーライドして、HibernateConfigurationクラスではなくConfigurationクラスのインスタンスを返すようにします。

ソリューション2を実行しました。少し作業が必要でしたが、現在は正常に実行されています。(Hibernateのバージョンを再度変更する場合は、少し作業が必要になる可能性があります。)

于 2012-09-18T16:09:55.163 に答える
1

org.hibernate.internal.util.ClassLoaderHelperをご覧ください。

あなたがしなければならないのは、ClassLoaderをエンティティクラスを解決できるClassLoaderに置き換えることだけです。Hibernate-OsgiもそれをOSGIClassLoaderに設定しています(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 に答える
1

Hibernate OSGiには現在いくつかの注意点があり、そのうちの1つには単一の永続性ユニットクライアントバンドルが必要です。さまざまな理由から、永続性エンティティ、マッピング、およびリソースの処理を担当する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ファイル自体の場所以外に、どのバンドルが永続性ユニットを構成しているかを知るための適切な方法はありません。

ただし、このような設定をより適切にサポートする方法についてのアイデアを聞きたいです。私はもともと、マニフェストに「私は永続性ユニットxの一部です」という意味の追加のメタデータを試してみましたが、実際にそれを熟考する時間はありませんでした。

ClassLoaderHelperを使用して、上記で推奨されているソリューションを絶対に使用しないでください。これは、ORM 5で行われる完全に一時的なハックです。これは、純粋にORM4の静的な性質によるものです。

于 2013-10-29T22:37:48.520 に答える