2012-09-18 52 views
3

我的要求是映射各種數據庫(特別是SQL Server,MySQl和Postgres)使用休眠;從db記錄創建一個xml文件。OSGi碎片捆綁與Hibernate運行時間pojos

對於休眠我使用JAssist在運行時創建hbm文件和pojos。 我的代碼工作得很好,爲了進一步的模塊化,我爲每個數據庫實現了片段捆綁,因此我的主機捆綁將處理運行時類的創建並將它們添加到類加載器,hbm文件創建邏輯和BL中。片段通過傳遞參數來調用它。

當我爲每個數據庫創建一個片段捆綁, 在我的主機捆綁創建運行時POJO類是在我的片段捆綁可見, 我檢查與「Thread.currentThread()。getContextClassLoader()。loadClass()會」 並能夠創建它的實例,

問題是 當我叫休眠功能,從片段包,我正在「實體未映射」,據我所知,當Hibernate是無法找到與表中的映射類,這些異常來。 所以我想Hibernate沒有找到我的運行時pojo類。它可以在主機中找到它。

主持人: 運行POJO的創作, HBM和CFG創建和更新用邏輯 BL

片段: 休眠層, 調用休眠功能, XML創建邏輯

回答

3

這個問題總是出現,如果你在多個bundle上使用Hibernate。在Hibernate配置中,無法知道哪個Bundle中可以找到映射文件和pojo類文件。 Hibernate不使用OSGI爲此提供的機制。因此,hibernate僅查找與Hibernate庫位於同一個包中的映射文件和類。

我不知道是否有任何地方有專業解決方案(第三方產品)解決此問題。

有兩種方法可以解決這個問題:

  1. 忘記片段包,並把所有Hibernate庫,映射文件的POJO,使用Hibernate/HQL所有數據庫類成一束。當您使用不同的hibernate.cfg.xml文件時,您可以在不同的數據庫之間切換;每個數據庫都有自己的配置文件。這些hibernate.cfg.xml文件可以在bundle之外。

  2. 自己寫的配置類擴展org.hibernate.cfg.Configuration,在這個類中,你必須

    • 寫自己的類加載器,其認爲即使是在其他包
    • 覆蓋的POJO類addResource(String resourceName,ClassLoader classLoader)以某種方式發現其他包中的資源
    • 覆蓋doConfigure和buildSessionFactory,因此它們使用您的類加載器而不是標準類加載器(使用Thread.setContextClassLoader並從超類調用方法,即從標準的Hibernate配置類)。
    • 覆蓋所有其他返回配置實例的方法,以便它們返回Configuration類的實例而不是Hibernate Configuration類的實例。

我們做的解決方案2.這是一些工作,但現在它運行良好。 (想想,當再次改變休眠版本時可能需要一點工作。)

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

我把這個給我HibernateConfiguration類通過重寫buildSessionFactory執行該程序,並返回super.buildSessionFactory。

的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。

1

Hibernate OSGi目前有幾個注意事項,其中之一需要一個持久單元客戶端軟件包。出於各種原因,當我們構建負責處理持久化實體,映射和資源的ClassLoader時,我們必須使用「requestsBundle」。

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

OsgiPersistenceProviderService和OsgiSessionFactoryService都添加了「requestingBundle」的類加載器服務時被調用。正如約翰娜所說,除了請求包或persistence.xml文件本身的位置外,沒有一個真正的方法可以知道哪些Bundle組成了持久性單元。

但是,我很想聽聽有關如何更好地支持這種設置的想法。我最初在清單中使用額外的元數據來表示「我是持久性單元x的一部分」,但從來沒有真正有時間去思考它。

肯定做不是使用上面推薦的解決方案使用ClassLoaderHelper。這是一個完全暫時的破解,它將在ORM 5中消失。純粹是由於ORM 4的靜態特性。

+0

創建https://hibernate.atlassian.net/browse/HHH-8658 - 歡迎評論。 – brmeyer

+0

感謝brmeyer獲取有價值的信息,但我們轉移到了jdbc。 – sailor