Использование Grails 1.3.7 с поставщиком EclipseLink 2.2.0 JPA - PullRequest
1 голос
/ 01 сентября 2011

Я пытаюсь использовать Grails 1.3.7 с плагином gorm-jpa 0.7.1, чтобы включить постоянство JPA, используя EclipseLink 2.2.0 в качестве поставщика постоянства.

При запуске приложения и нажатии на контроллереАннотируемая сущность JPA Я получаю «UnwantedTokenException» от JPQLParser EclipseLink:

Executing action [list] of controller [model.PersonController] caused exception:
An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [select person from model.Person as person ], line 1, column 24: syntax error at [.].
Internal Exception: UnwantedTokenException(found=., expected 80);
  at org.eclipse.persistence.internal.libraries.antlr.runtime.BaseRecognizer.recoverFromMismatchedToken(BaseRecognizer.java:587)
  [...]
  at org.eclipse.persistence.internal.jpa.parsing.jpql.JPQLParser.parse(JPQLParser.java:134)

Похоже, что EclipseLink имеет проблему с точкой в ​​"model.Person".

Как решить эту проблемуПроблема?

Чтобы воспроизвести проблему, настройте проект Grails следующим образом:

grails create-app GrailsJPA
cd GrailsJPA
grails uninstall-plugin hibernate
grails install-plugin gorm-jpa
grails create-domain-class model.Person

Отредактируйте "grails-app \ domain \ model \ Person.groovy" следующим образом:

package model

import javax.persistence.*;

@Entity
class Person {

    @Id
    @GeneratedValue
    long id;

    @Basic
    long version;

    @Basic
    String firstName

    @Basic
    String lastName

    static constraints = {
        firstName(nullable:true)
        lastName(nullable:true)
    }

}

Создание контроллера и представление для определенной сущности:

grails generate-controller model.Person
grails generate-view model.Person

Отредактируйте "grails-app \ conf \ spring \ resources.groovy" следующим образом:

beans = {

    entityManagerFactory(org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean) {
        beanClassLoader = ref("classLoader")
        dataSource = ref("dataSource")
        loadTimeWeaver = new org.springframework.instrument.classloading.SimpleLoadTimeWeaver()
    }

    transactionManager(org.springframework.orm.jpa.JpaTransactionManager) {
        entityManagerFactory = entityManagerFactory
    }

}

Создайтефайл "web-app \ WEB-INF \ classes \ META-INF \ persistence.xml" выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
    <persistence-unit name="manager" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>model.Person</class>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
        </properties>
    </persistence-unit>
</persistence>

Загрузите установочный файл EclipseLink 2.2.0 ZIP из http://www.eclipse.org/eclipselink/downloads/2.2.0 и извлеките "eclipselink".jar "из" eclipselink \ jlib \ "в ZIP-архиве с проектом grails: dir:

lib\eclipselink.jar

Now rВ приложении Grails:

grails run-app

Перейдите к

http://localhost:8080/GrailsJPA

Теперь нажмите на контроллер "model.PersonController", чтобы воспроизвести вышеприведенное исключение.

Любые идеи о том, какрешить эту проблему?

1 Ответ

1 голос
/ 03 сентября 2011

Мне удалось заставить поставщика EclipseLink 2.3.0 JPA работать с классами домена, которые не внутри пакета, следующим образом.

Далее я покажу, как настроить приложение grails, использующее EclipseLink в качестве поставщика JPA, для сохранения классов домена и запуска приложения в веб-контейнере Tomcat 6.

Начните с

  • создание нового проекта Grails
  • удалить плагин hibernate по умолчанию
  • установить плагин gorm-jpa, который включает gorm-подобное поведение поверх JPA
  • создать новый домен класса «Персона» для тестирования

    grails create-app GrailsJPA
    cd GrailsJPA
    grails uninstall-plugin hibernate
    grails install-plugin gorm-jpa
    grails create-domain-class Person
    

Подсказка: не помещайте доменные классы в пакеты. EclipseLink потерпит неудачу при обработке, например, "model.Person" из-за точки в имени, но будет хорошо, например, с. «Персона» (сравните с вышеупомянутым постом).

Отредактируйте "grails-app \ domain \ Person.groovy" следующим образом:

import javax.persistence.*;

@Entity
class Person {

    @Id
    @GeneratedValue
    long id;

    @Basic
    long version;

    @Basic
    String firstName

    @Basic
    String lastName

    static constraints = {
        firstName(nullable:true)
        lastName(nullable:true)
    }

}

Создание контроллера и представления для определенной сущности:

grails generate-controller Person
grails generate-view Person

Теперь нам нужно немного изменить сгенерированный контроллер, чтобы все действия, модифицирующие сущность (сохранение, редактирование, обновление, удаление), были инкапсулированы в транзакцию (сравните с ошибкой GPGORMJPA-6). Это делается путем инкапсуляции всего действия в блок «Person.withTransaction».

Измените "grails-app \ controllers \ PersonController.groovy" следующим образом:

[...]
def save = {
    Person.withTransaction {
    [...original code stays in this block...]
    }
}
[...]
def edit = {
    Person.withTransaction {
        [...original code stays in this block...]           }
    }
}
def update = {
    Person.withTransaction {
        [...original code stays in this block...]
    }
}
def delete = {
    Person.withTransaction {
        [...original code stays in this block...]
    }
}
[...]

Теперь мы определяем единицу сохраняемости "manager", которая задает набор классов для управления сохранением (класс "Person") и какой поставщик JPA будет использовать для этих классов (в нашем случае EclipseLink).

Создайте файл "web-app \ WEB-INF \ classes \ META-INF \ persistence.xml" следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
    <persistence-unit name="manager" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>Person</class>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
        </properties>
    </persistence-unit>
</persistence>

Обратите внимание: файл будет удален, если очищен «целевой» каталог проекта, например, когда "grails clean" вызывается явно. Так что неплохо сделать резервную копию файла вне «целевой» директории.

Чтобы сделать EclipseLink доступным для проекта grails, загрузите ZIP-файл установщика EclipseLink 2.3.0 и извлеките «eclipselink.jar» из «eclipselink \ jlib \» в ZIP-папку «lib» проекта grails:

lib\eclipselink.jar

Теперь нам нужно убедиться, что bean-объект "entityManagerFactory" и "actionManager "создаются при запуске веб-приложения. Бины предоставят необходимый доступ JPA-провайдеру, который будет управлять всей устойчивостью.

Отредактируйте "grails-app \ conf \ spring \ resources.groovy" следующим образом:

beans = {

    entityManagerFactory(org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean) {
        dataSource = ref("dataSource")
        beanClassLoader = ref("classLoader")
        loadTimeWeaver = new org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver()
    }

    transactionManager(org.springframework.orm.jpa.JpaTransactionManager) {
        entityManagerFactory = entityManagerFactory
    }

}

Обратите внимание, что в указанных выше объявлениях мы указали специальный loadTimeWeaver, который позволяет поставщику постоянства JPA подключаться к байт-коду java на лету во время выполнения. При использовании EclipseLink это важно. Если просто использовать org.springframework.instrument.classloading.SimpleLoadTimeWeaver для целей тестирования, это позволяет запустить веб-приложение, но при доступе к сущностям JPA вы столкнетесь с такими исключениями, как «0 не является известным типом сущности», так как поставщик сохраняемости не может подключиться к управлению классами сущностей во время выполнения.

Использование InstrumentationLoadTimeWeaver немного хлопотно, так как будет работать, только если

  • виртуальная машина java, выполняющая сервер веб-приложений, запускается с использованием java-агента «spring-agent», AND
  • наше веб-приложение загружается сервером Tomcat с помощью специального загрузчика классов с именем "TomcatInstrumentableClassLoader"

Для этого сначала загрузите spring-agent-2.5.6.SEC02.jar и spring-instrument-tomcat-3.0.5.RELEASE.jar .

Предположим, что вы установили сервер Tomcat в каталог% CATALINA_HOME%.

  • Скопируйте загруженные файлы jar "spring-agent-2.5.6.SEC02.jar" и "spring-instrument-tomcat-3.0.5.RELEASE.jar" в% CATALINA_HOME% \ lib

Теперь мы изменим "% CATALINA_HOME% \ bin \ catalina.bat", который используется, например, сценарии запуска и остановки Tomcat, чтобы убедиться, что JVM, выполняющая Tomcat, работает с Java-агентом «spring-agent».

Добавьте следующее в "% CATALINA_HOME% \ bin \ catalina.bat" в разделе "Выполнить требуемую команду" после всех эхосигналов:

if exist "%CATALINA_HOME%\lib\spring-agent-2.5.6.SEC02.jar" set JAVA_OPTS=%JAVA_OPTS% -javaagent:%CATALINA_HOME%\lib\spring-agent-2.5.6.SEC02.jar

Когда Tomcat запущен, скрипт теперь проверит, находится ли «spring-agent-2.5.6.SEC02.jar» в директории lib, и если это так, он добавит его в качестве агента java к JAVA_OPTS, которые используются в качестве параметров командной строки для JVM при запуске Tomcat.

Чтобы включить «TomcatInstrumentableClassLoader» в качестве загрузчика классов для нашего веб-приложения, мы добавляем файл «web-app \ META-INF \ context.xml» в наш проект grails со следующим содержимым:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

Обратите внимание, что теги в "context.xml" чувствительны к регистру. Поэтому не пытайтесь изменить <<b> C ontext> на <<b> c ontext> (или аналогичные вещи) - это не удастся!

Перед тем, как упаковать и развернуть наше веб-приложение, мы определяем, какой источник данных использовать для обеспечения устойчивости. Для тестирования мы просто определяем базу данных HSQLDB in-memeory.

Отредактируйте "grails-app \ conf \ DataSource.groovy" следующим образом:

[...]
development {
    dataSource {
        dbCreate = "create-drop"
        url = "jdbc:hsqldb:mem:testDB"
    }
}
test {
    dataSource {
        dbCreate = "create-drop"
        url = "jdbc:hsqldb:mem:testDb"
    }
}
production {
    dataSource {
        dbCreate = "create-drop"
        url = "jdbc:hsqldb:mem:testDB"
    }
}
[...]

Теперь мы готовы создать WAR-архив нашего веб-приложения, используя:

grails war

WAR будет помещен в «целевой» каталог нашего проекта.

В "% CATALINA_HOME% \ webapps"

  • создать каталог "GrailsJPA"
  • извлечь все содержимое созданного архива WAR в этот каталог

Обратите внимание: не помещайте WAR в "% CATALINA_HOME% \ webapps" и надейтесь, что он развернут при запуске Tomcat. Это не удастся!

Теперь запустите сервер Tomcat, используя скрипт

"%CATALINA_HOME%\bin\startup.bat"

После того, как Tomcat запущен и работает, используйте ваш браузер, чтобы перейти к

http://localhost:8080/GrailsJPA

Теперь нажмите на контроллер «PersonController», чтобы создавать, обновлять, перечислять и удалять сущности Person через JPA с использованием поставщика EclipseLink JPA.

Но с использованием классов домена, которые находятся внутри пакета, все равно потерпит неудачу за исключением упомянутого выше поста. Эта проблема до сих пор не решена.

Ссылки для скачивания

...