JPA/EclipseLink: понимание проблем ClassLoader

РЕДАКТИРОВАТЬ: хотя был дан и присужден (хороший) ответ, он охватывает лишь довольно незначительную часть моего вопроса. Основные части этого вопроса остаются открытыми.

Я использую EclipseLink (2.6.2) в облачном проекте. Проект представляет собой веб-приложение, упакованное в виде файла WAR и развернутое на Apache Tomcat 8. Контекст персистентности настраивается с помощью кода Java, где я указываю сущности для использования с помощью entityManagerFactoryBean.setPackagesToScan(packagesToScan). Эта конфигурация обычно работает так, как ожидается, где точно находятся классы сущностей в указанных пакетах.

Теперь я не понимаю, когда использовать какой загрузчик классов, особенно при рассмотрении Tomcat, выполнении тестов и использовании различных реализаций пула соединений.

При работе на Apache Tomcat, включая пул соединений Tomcat, экземпляр DataSource создается с помощью подключаемого модуля spring-cloud-connector (spring-cloud-spring-service-connector). В этом параметре все работает так, как ожидалось, пока я не изменю загрузчик классов, как описано ниже (в противном случае я столкнусь с ClassNotFoundExceptions для классов сущностей).

При выполнении модульных тестов с помощью JUnit и spring-test экземпляр DataSource создается с использованием базы данных H2 в памяти (используя EmbeddedDatabaseBuilder из spring-jdbc). В этом параметре я должен указать JPA для использования загрузчика классов, используемого для экземпляра DataSource (ключ eclipselink.classloader на карте свойств JPA), иначе я получаю «Объект... не является известным типом объекта».

При выполнении тестов во встроенном Apache Tomcat 8 я не вижу никаких сообщений, указывающих на используемый пул соединений. В этом параметре я также должен установить загрузчик классов для модульных тестов.

Если я добавлю commons-dbcp (2.1.1) в свой проект и явно настрою подключаемый модуль spring-cloud-connector для использования его вместо пула соединений Tomcat, я смогу запустить приложение на Tomcat без настройки загрузчика классов, но оно также работает с описанной выше спецификацией загрузчика классов.

Для тестов commons-dbcp ничего не меняет по сравнению со сценариями, описанными выше (поскольку соответствующая конфигурация не используется).

Резюме:

  • Tomcat (Tomcat CP): использование только немодифицированного загрузчика классов для JPA
  • Tomcat (DBCP): оба варианта
  • Тесты: использование только загрузчика классов DataSource для JPA

Не могли бы вы помочь мне понять различия здесь и предложить простое решение, подходящее для всех случаев? Я предполагаю, что DBCP и Spring используют другой загрузчик классов, чем Tomcat (и пул соединений Tomcat).

Если вам нужна дополнительная информация, я с радостью добавлю ее.

РЕДАКТИРОВАТЬ: я добавил пример проекта с большим README о том, как воспроизвести.

https://github.com/C-Otto/classloaderexample


person C-Otto    schedule 27.04.2016    source источник
comment
Также взгляните на stackoverflow.com/questions/35318985/   -  person C-Otto    schedule 06.05.2016
comment
Я не понимаю вашего вопроса. Ваши вопросы связаны с DataSource, Connection Pool и Service Discovery (spring-cloud-service-*). Я думаю, что вам лучше всего создать тематическое исследование, используя простой проект в github, и задать свои точные проблемы здесь.   -  person xsalefter    schedule 11.05.2016
comment
@xsalefter Я добавил пример проекта с некоторой документацией. Спасибо за предложение.   -  person C-Otto    schedule 12.05.2016
comment
Только что обнаружил, почему ваш tomcat:run не работает с вашим CloudDatabaseConfig. Вам необходимо настроить зависимость postgresql от плагина tomcat7-maven. Я постараюсь найти другие ошибки сейчас.   -  person fhofmann    schedule 12.05.2016
comment
@fhofmann на самом деле работает, просто не во всех вариантах конфигурации. Что вы имеете в виду под настройкой зависимости postgresql?   -  person C-Otto    schedule 12.05.2016
comment
Я не мог воспроизвести ошибки тестов. Я получаю другое: com.test.BaseEntity не является известным типом Entity. Надеюсь, что мой ответ помог.   -  person fhofmann    schedule 13.05.2016
comment
Ошибка, которую вы упомянули, это именно то, что я вижу. Можешь объяснить? Почему мне нужно установить другой загрузчик классов?   -  person C-Otto    schedule 13.05.2016


Ответы (1)


«Я получаю еще одну ошибку, когда начинаю использовать mvn tomcat7: run, использовать пул соединений Tomcat (CloudDatabaseConfig) и не перенастраивать classloder (JpaConfig):»

Вы должны настроить зависимость PostreSQL от плагина maven. Вы получаете ClassNot found, потому что JAR PostgreSQL не указан в пути:

<plugin>
  <groupId>org.apache.tomcat.maven</groupId>
  <artifactId>tomcat7-maven-plugin</artifactId>
  <version>2.2</version>
  <configuration>
    <port>8080</port>
    <path>/</path>
  </configuration>
  <!-- For any extra dependencies needed when running embedded Tomcat (not WAR dependencies) add them below -->
  <dependencies>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>9.4-1206-jdbc41</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</plugin>

Также взгляните на https://tomcat.apache.org/maven-plugin-2.2/run-mojo-features.html

Я забыл включить изменение загрузчика классов (JpaConfigWithDatasourceClassloader.getJPAProperties):

properties.put(CLASSLOADER, new java.net.URLClassLoader(
   ((java.net.URLClassLoader)classLoader).getURLs(), JpaConfigWithDatasourceClassloader.class.getClassLoader()  ) 
) ;

При этом вы можете запускать все варианты, используя tomcat7:run

person fhofmann    schedule 12.05.2016
comment
Спасибо, я не знал о зависимостях плагинов. Я добавил некоторую информацию к вашему ответу, это действительно помогло. Что касается проблемы с загрузчиком классов: мне кажется, что ваш фрагмент просто показывает другой способ настройки загрузчика классов для использования JPA. Я до сих пор не понимаю, зачем вообще нужна эта конфигурация и почему она вызывает проблемы в зависимости от пула соединений и настройки запуска/тестирования. - person C-Otto; 13.05.2016
comment
Я не эксперт в загрузчике классов Tomcat, но, глядя на код, я представляю, что источник данных загружается с помощью загрузчика классов контейнера, а ваши объекты - с загрузчиком классов приложения. Если вы передадите контейнер classloder в JPA, он не увидит claases, которые у вас есть в вашей войне. - person fhofmann; 13.05.2016
comment
Объект DataSource создан внутри моего приложения, так что этого не может быть. Но очень вероятно, что реализация пула соединений была создана с помощью другого загрузчика классов, и это вызывает проблемы. - person C-Otto; 13.05.2016