Equinox (OSGi) и JPA / Hibernate - поиск сущностей

Я пытаюсь использовать Hibernate / Spring в среде OSGi (Equinox). Он отлично работает, если я явно укажу на классы Entity в Persistence.xml:

    <class>com.es.t.eee.domain.StuffSource</class>
    <class>com.es.t.eee.domain.PostalAddress</class>

Я хочу, чтобы Hibernate «нашел» все классы Entity так же, как он это делает вне среды OSGi.

Hibernate ищет правильный пакет для @Entities:

Searching mapped entities in jar/par: bundleresource://34/
WARN  27-07 15:30:24,235 (InputStreamZippedJarVisitor.java:doProcessElements:41):
Unable to find file (ignored): bundleresource://34/

Похоже, это должно работать, но когда доходит до поиска в Bundle Jar для @Entities, возникает исключение, и я не уверен, почему. Я включил важные части журнала, которые выводит Hibernate.

Есть ли у кого-нибудь идеи, что я делаю не так или в чем проблема?

Я использую:

  • Hibernate Core 3.3.0.SP1
  • Аннотации Hibernate 3.4.0.GA
  • Аннотации Hibernate Commons 3.1.0.GA
  • Hibernate EntityManager 3.4.0.GA
  • Равноденствие 3.4
  • Динамические модули Spring 1.2.0

Здесь Hibernate анализирует файл Persistence.xml

INFO  27-07 15:30:24,110 (Version.java:<clinit>:15):
Hibernate Annotations 3.4.0.GA
INFO  27-07 15:30:24,110 (Environment.java:<clinit>:543):
Hibernate 3.3.0.SP1
INFO  27-07 15:30:24,110 (Environment.java:<clinit>:576):
hibernate.properties not found
INFO  27-07 15:30:24,126 (Environment.java:buildBytecodeProvider:709):
Bytecode provider name : javassist
INFO  27-07 15:30:24,126 (Environment.java:<clinit>:627):
using JDK 1.4 java.sql.Timestamp handling
INFO  27-07 15:30:24,157 (Version.java:<clinit>:14):
Hibernate Commons Annotations 3.1.0.GA
INFO  27-07 15:30:24,157 (Version.java:<clinit>:16):
Hibernate EntityManager 3.4.0.GA
DEBUG 27-07 15:30:24,219 (Ejb3Configuration.java:configure:312):
Processing PersistenceUnitInfo [
    name: unit.postgresql
    persistence provider classname: null
    classloader: BundleDelegatingClassLoader for [DatabaseObjects (DatabaseObjects)]
    Temporary classloader: org.springframework.instrument.classloading.SimpleThrowawayClassLoader@11b50a1
    excludeUnlistedClasses: true
    JTA datasource: null
    Non JTA datasource: com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 1000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1012f6d821goxcok12zcw86|174f02c, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> org.postgresql.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1012f6d821goxcok12zcw86|174f02c, idleConnectionTestPeriod -> 0, initialPoolSize -> 5, jdbcUrl -> jdbc:postgresql://localhost:5432/test2, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 100, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
    Transaction type: RESOURCE_LOCAL
    PU root URL: bundleresource://34/
    Jar files URLs []
    Managed classes names []
    Mapping files names []
    Properties [
        hibernate.default_batch_fetch_size: 500
        hibernate.cache.provider_class: net.sf.ehcache.hibernate.EhCacheProvider
        hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
        hibernate.max_fetch_depth: 5
        hibernate.query.factory_class: org.hibernate.hql.ast.ASTQueryTranslatorFactory
        hibernate.format_sql: false
        hibernate.jdbc.batch_size: 1000
        hibernate.use_outer_join: true
        hibernate.archive.autodetection: class
        hibernate.show_sql: false
        hibernate.bytecode.provider: cglib]

Вот где возникает ошибка, когда он пытается найти объекты:

DEBUG 27-07 15:30:24,235 (Ejb3Configuration.java:getDetectedArtifacts:562):
Detect class: true; detect hbm: false
DEBUG 27-07 15:30:24,235 (Ejb3Configuration.java:getDetectedArtifacts:562):
Detect class: true; detect hbm: false
DEBUG 27-07 15:30:24,235 (AbstractJarVisitor.java:unqualify:116):
Searching mapped entities in jar/par: bundleresource://34/
WARN  27-07 15:30:24,235 (InputStreamZippedJarVisitor.java:doProcessElements:41):
Unable to find file (ignored): bundleresource://34/
java.lang.NullPointerException: in is null
    at java.util.zip.ZipInputStream.<init>(Unknown Source)
    at java.util.jar.JarInputStream.<init>(Unknown Source)
    at java.util.jar.JarInputStream.<init>(Unknown Source)
    at org.hibernate.ejb.packaging.InputStreamZippedJarVisitor.doProcessElements(InputStreamZippedJarVisitor.java:37)
    at org.hibernate.ejb.packaging.AbstractJarVisitor.getMatchingEntries(AbstractJarVisitor.java:139)
    at org.hibernate.ejb.Ejb3Configuration.addScannedEntries(Ejb3Configuration.java:287)
    at org.hibernate.ejb.Ejb3Configuration.scanForClasses(Ejb3Configuration.java:614)
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:360)
    at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:131)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:224)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:291)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isSingleton(AbstractBeanFactory.java:366)
    at org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean.afterPropertiesSet(OsgiServiceFactoryBean.java:235)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:423)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
    at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.completeRefresh(AbstractDelegatedExecutionApplicationContext.java:288)
    at org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor$CompleteRefreshTask.run(DependencyWaiterApplicationContextExecutor.java:145)
    at java.lang.Thread.run(Unknown Source)

person mainstringargs    schedule 27.07.2009    source источник
comment
Я не специалист по этому вопросу, но: это просто ПРЕДУПРЕЖДЕНИЕ. Вы уверены, что он не работает, несмотря на эту трассировку стека? Вы получаете предупреждение, потому что Hibernate ожидает, что URL-адрес корня PU будет банкой, но это не так. Если это действительно не работает, я подозреваю, что у Rich Seller есть правильная идея.   -  person Zac Thompson    schedule 31.07.2009
comment
Я точно знаю, что он не видит сущностей, потому что, когда я пытаюсь выполнить запрос, он говорит, что тип не отображается.   -  person mainstringargs    schedule 31.07.2009


Ответы (2)


В этом блоге показано, как с этим можно справиться с помощью Динамические модули Spring. На основе этого блога вы создадите определения bean-компонентов для DAO и сервисных bean-компонентов в контексте вашего приложения. В DAO есть ссылка на bean-компонент hibernate sessionFactory:

<bean id="stuffDao" class="com.es.t.eee.domain.dao.StuffSourceDaoImpl">
  <property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="stuffService" 
  class="com.es.t.eee.domain.dao.StuffServiceImpl">
  <property name="stuffDao" ref="stuffDao"/>
</bean>

<bean id="stuffSource" 
  class="com.es.t.eee.domain.StuffSource" scope="prototype"/>

StuffSourceDaoImpl будет реализовывать метод store () для сохранения StuffSource, он передает StuffSource в HibernateTemplate для фактического сохранения.

Чтобы избежать необходимости определять файлы persistence.xml или указывать каждую сущность, вы можете использовать AnnotationSessionFactoryBean и подстановочный знак, чтобы включить все типы в пакет (ы), содержащий ваши сущности (вместо этого вы можете захотеть установить свойство packagesToScan, я не пробовал, и это может вернуть вас к исходной проблеме):

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="hibernateProperties">
    <props>
      <!--etc-->
    </props>
  </property>
  <!-- set either one of these properties -->
  <property name="annotatedClasses">
    <list>
      <value>com.es.t.eee.domain.StuffSource</value>
      <value>com.es.t.eee.domain.PostalAddress</value>
    </list>
  </property>
  <property name="packagesToScan">
    <list>
      <value>com.es.t.eee.domain</value>
    </list>
  </property>
</bean>

Чтобы использовать это, вы должны получить BundleContext, получить службу и компонент из контекста, а затем продолжить как обычно:

String serviceName = StuffService .class.getName();
StuffService service = 
  (StuffService )context.getService(context.getServiceReference(serviceName));

String stuffName = StuffSource.class.getName();
StuffSource stuffSource = 
  (StuffSource) context.getService(context.getServiceReference(stuffName ));

//do whatever with StuffSource
...

service.store(stuffSource );

Вы также можете посетить этот блог OSGi Alliance в котором обсуждаются некоторые из затронутых вопросов. Из блога OSGi Alliance:

Hibernate управляет путем к классам, и подобные программы обычно плохо работают вместе с системами на основе OSGi. Причина в том, что во многих системах видимость классов между модулями более или менее ограничена. В каркасах OSGi путь к классам четко определен и ограничен. Это дает нам много хороших функций, но также причиняет боль, когда мы хотим использовать библиотеку, которая стремится стать загрузчиком классов, когда вырастет. ...

Для работы с Hibernate вам понадобится объект Session. Вы можете получить объект Session из SessionFactory. Чтобы получить SessionFactory, вам необходимо создать его с помощью объекта Configuration. Объект конфигурации создается из файла конфигурации XML. По умолчанию Hibernate загружает это из корня вашего JAR-файла, однако при желании вы можете добавить классы в конфигурацию вручную.

person Rich Seller    schedule 30.07.2009

Предыдущий ответ, в котором упоминалась запись в блоге OSGi о проблемах Hibernate и OSGi, является информативным.

Я предлагаю сделать шаг назад и подумать о предположениях, которые верны в традиционном приложении Java, и о том, что они больше не действуют в OSGi:

OSGi динамичный

Как будет работать ваше приложение, если пакет Hibernate будет запущен до любых пакетов, которые предоставляют классы сущностей? Даже если бы сканирование классов сущностей сработало, ничего бы не было.

В традиционной Java все классы доступны при запуске из-за плоского пути к классам, но в OSGi классы доступны только в том случае, если вспомогательный пакет запущен и вы импортируете пакеты.

Явные зависимости

OSGi требует явного связывания зависимостей; если вы не импортируете бандл / пакет, тогда вы не сможете увидеть классы в этих бандлах / пакетах.

Поскольку вы говорите, что явная настройка классов сущностей работает, я предполагаю, что вы используете одно из следующих действий для связывания зависимостей:

  • Импорт-Пакет
  • Требовать-Bundle
  • Динамический импорт
  • Политика друзей Equinox

Предложение Rich Seller должно работать (я его не тестировал), но вы все равно вынуждены импортировать пакеты, содержащие классы сущностей, хотя я не вижу в этом ничего плохого, и предлагаемое решение дает возможность сканирования пакетов, а не явно указывать каждый единый класс сущности.

Предложения

  1. Используйте решение Rich Seller, но имейте в виду, что решение не является динамическим (это может не иметь значения для вашего приложения).
  2. Внедрите шаблон расширения для классов сущностей, чтобы можно было воссоздать фабрику сеансов Hibernate при запуске и остановке пакетов, вносящих сущность.

Теперь вариант 1 прост, а вариант 2 требует много работы. Все зависит от ваших требований к автоматическому добавлению / удалению классов сущностей из Hibernate без необходимости явно указывать пакеты / классы.

person SteveD    schedule 02.08.2009